This commit is contained in:
parent
a47da023c9
commit
893588a0ce
14 changed files with 450 additions and 299 deletions
|
|
@ -346,6 +346,10 @@ def set_value(doctype, docname, fieldname, value):
|
|||
def get_doclist(doctype, name=None):
|
||||
return bean(doctype, name).doclist
|
||||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -6,9 +6,15 @@ import frappe
|
|||
|
||||
from frappe.utils import getdate, cint, add_months, date_diff, add_days, nowdate
|
||||
from frappe.core.doctype.user.user import STANDARD_USERS
|
||||
from frappe.model.document import Document
|
||||
|
||||
weekdays = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"]
|
||||
|
||||
class Event(Document):
|
||||
def validate(self):
|
||||
if self.starts_on and self.ends_on and self.starts_on > self.ends_on:
|
||||
frappe.msgprint(frappe._("Event End must be after Start"), raise_exception=True)
|
||||
|
||||
class DocType:
|
||||
def __init__(self, d, dl):
|
||||
self.doc, self.doclist = d, dl
|
||||
|
|
|
|||
242
frappe/model/base_document.py
Normal file
242
frappe/model/base_document.py
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
# MIT License. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _, msgprint
|
||||
from frappe.utils import cint, flt, cstr, now
|
||||
from frappe.model import default_fields
|
||||
from frappe.model.db_schema import type_map
|
||||
from frappe.model.naming import set_new_name
|
||||
|
||||
class BaseDocument(object):
|
||||
def __init__(self, d, valid_columns=None):
|
||||
self.update(d, valid_columns=valid_columns)
|
||||
|
||||
def __getattr__(self, key):
|
||||
if self.__dict__.has_key(key):
|
||||
return self.__dict__[key]
|
||||
|
||||
if key!= "_valid_columns" and key in self.get_valid_columns():
|
||||
return None
|
||||
|
||||
raise AttributeError(key)
|
||||
|
||||
def update(self, d, valid_columns=None):
|
||||
if valid_columns:
|
||||
self.__dict__["_valid_columns"] = valid_columns
|
||||
if "doctype" in d:
|
||||
self.set("doctype", d.get("doctype"))
|
||||
for key, value in d.iteritems():
|
||||
self.set(key, value)
|
||||
|
||||
def get(self, key=None, filters=None, limit=None, default=None):
|
||||
if key:
|
||||
if filters:
|
||||
return _filter(self.__dict__.get(key), filters, limit=limit)
|
||||
else:
|
||||
return self.__dict__.get(key, default)
|
||||
else:
|
||||
return self.__dict__
|
||||
|
||||
def set(self, key, value, valid_columns=None):
|
||||
if isinstance(value, list):
|
||||
tmp = []
|
||||
for v in value:
|
||||
tmp.append(self._init_child(v, key, valid_columns))
|
||||
value = tmp
|
||||
|
||||
self.__dict__[key] = value
|
||||
|
||||
def append(self, key, value):
|
||||
if isinstance(value, dict):
|
||||
if not self.get(key):
|
||||
self.__dict__[key] = []
|
||||
self.get(key).append(self._init_child(value, key))
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
def extend(self, key, value):
|
||||
if isinstance(value, list):
|
||||
for v in value:
|
||||
self.append(v)
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
def _init_child(self, value, key, valid_columns=None):
|
||||
if not self.doctype:
|
||||
return value
|
||||
if not isinstance(value, BaseDocument):
|
||||
if not value.get("doctype"):
|
||||
value["doctype"] = self.get_table_field_doctype(key)
|
||||
if not value.get("doctype"):
|
||||
raise AttributeError, key
|
||||
value = BaseDocument(value, valid_columns=valid_columns)
|
||||
|
||||
value.parent = self.name
|
||||
value.parenttype = self.doctype
|
||||
value.parentfield = key
|
||||
if not value.idx:
|
||||
value.idx = len(self.get(key) or []) + 1
|
||||
|
||||
return value
|
||||
|
||||
@property
|
||||
def doc(self):
|
||||
return self
|
||||
|
||||
@property
|
||||
def meta(self):
|
||||
if not self.get("_meta"):
|
||||
self._meta = frappe.get_meta(self.doctype)
|
||||
return self._meta
|
||||
|
||||
def get_valid_dict(self):
|
||||
d = {}
|
||||
for fieldname in self.valid_columns:
|
||||
d[fieldname] = self.get(fieldname)
|
||||
return d
|
||||
|
||||
@property
|
||||
def valid_columns(self):
|
||||
return self.get_valid_columns()
|
||||
|
||||
def get_valid_columns(self):
|
||||
if not hasattr(self, "_valid_columns"):
|
||||
doctype = self.__dict__.get("doctype")
|
||||
self._valid_columns = default_fields[1:] + \
|
||||
[df.fieldname for df in frappe.get_meta(doctype).get("fields")
|
||||
if df.fieldtype in type_map]
|
||||
|
||||
return self._valid_columns
|
||||
|
||||
def get_table_field_doctype(self, fieldname):
|
||||
return self.meta.get("fields", {"fieldname":fieldname})[0].options
|
||||
|
||||
def db_insert(self):
|
||||
set_new_name(self)
|
||||
d = self.get_valid_dict()
|
||||
columns = d.keys()
|
||||
frappe.db.sql("""insert into `tab{doctype}`
|
||||
({columns}) values ({values})""".format(
|
||||
doctype = self.doctype,
|
||||
columns = ", ".join(["`"+c+"`" for c in columns]),
|
||||
values = ", ".join(["%s"] * len(columns))
|
||||
), d.values())
|
||||
self.set("__islocal", False)
|
||||
|
||||
def db_update(self):
|
||||
d = self.get_valid_dict()
|
||||
columns = d.keys()
|
||||
frappe.db.sql("""update `tab{doctype}`
|
||||
set {values} where name=%s""".format(
|
||||
doctype = self.doctype,
|
||||
values = ", ".join(["`"+c+"`=%s" for c in columns])
|
||||
), d.values() + [d.get("name")])
|
||||
|
||||
def _fix_numeric_types(self):
|
||||
for df in self.meta.get("fields"):
|
||||
if df.fieldtype in ("Int", "Check"):
|
||||
self.set(df.fieldname, cint(self.get(df.fieldname)))
|
||||
elif df.fieldtype in ("Float", "Currency"):
|
||||
self.set(df.fieldname, flt(self.get(df.fieldname)))
|
||||
|
||||
if self.docstatus is not None:
|
||||
self.docstatus = cint(self.docstatus)
|
||||
|
||||
def set_missing_values(self, d):
|
||||
for key, value in d.iteritems():
|
||||
if self.get(key) is None:
|
||||
self.set(key, value)
|
||||
|
||||
def _get_missing_mandatory_fields(self):
|
||||
"""Get mandatory fields that do not have any values"""
|
||||
def get_msg(df):
|
||||
if df.fieldtype == "Table":
|
||||
return "{}: {}: {}".format(_("Error"), _("Data missing in table"), _(df.label))
|
||||
|
||||
elif self.parentfield:
|
||||
return "{}: {} #{}: {}: {}".format(_("Error"), _("Row"), self.idx,
|
||||
_("Value missing for"), _(df.label))
|
||||
|
||||
else:
|
||||
return "{}: {}: {}".format(_("Error"), _("Value missing for"), _(df.label))
|
||||
|
||||
missing = []
|
||||
|
||||
for df in self.meta.get("fields", {"reqd": 1}):
|
||||
if self.get(df.fieldname) in (None, []):
|
||||
missing.append((df.fieldname, get_msg(df)))
|
||||
|
||||
return missing
|
||||
|
||||
def get_invalid_links(self):
|
||||
def get_msg(df, docname):
|
||||
if self.parentfield:
|
||||
return "{} #{}: {}: {}".format(_("Row"), self.idx, _(df.label), docname)
|
||||
else:
|
||||
return "{}: {}".format(_(df.label), docname)
|
||||
|
||||
invalid_links = []
|
||||
for df in self.meta.get_link_fields():
|
||||
doctype = df.options
|
||||
|
||||
if not doctype:
|
||||
frappe.throw("Options not set for link field: {}".format(df.fieldname))
|
||||
|
||||
elif doctype.lower().startswith("link:"):
|
||||
doctype = doctype[5:]
|
||||
|
||||
docname = self.get(df.fieldname)
|
||||
if docname and not frappe.db.get_value(doctype, docname):
|
||||
invalid_links.append((df.fieldname, docname, get_msg(df, docname)))
|
||||
|
||||
return invalid_links
|
||||
|
||||
def _validate_constants(self):
|
||||
if frappe.flags.in_import:
|
||||
return
|
||||
|
||||
constants = [d.fieldname for d in self.meta.get("fields", {"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.get(fieldname) != values.get(fieldname):
|
||||
frappe.throw("{0}: {1}".format(_("Value cannot be changed for"),
|
||||
_(meta.get("fields", {"fieldname":fieldname})[0].label)),
|
||||
frappe.CannotChangeConstantError)
|
||||
|
||||
def _filter(data, filters, limit=None):
|
||||
"""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 d in data:
|
||||
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(d)
|
||||
if limit and (len(out)-1)==limit:
|
||||
break
|
||||
|
||||
return out
|
||||
|
|
@ -17,7 +17,7 @@ methods in following modules are imported for backward compatibility
|
|||
"""
|
||||
|
||||
import frappe
|
||||
from frappe.modules import get_doctype_module
|
||||
from frappe.modules import get_doctype_module, load_doctype_module, get_module_name
|
||||
import frappe.model.doc
|
||||
|
||||
def get_obj(dt = None, dn = None, doc=None, doclist=None, with_children = 0):
|
||||
|
|
@ -43,18 +43,6 @@ def get_server_obj(doc, doclist = [], basedoctype = ''):
|
|||
module = get_doctype_module(doc.doctype)
|
||||
return load_doctype_module(doc.doctype, module).DocType(doc, doclist)
|
||||
|
||||
def load_doctype_module(doctype, module=None, prefix=""):
|
||||
if not module:
|
||||
module = get_doctype_module(doctype)
|
||||
return frappe.get_module(get_module_name(doctype, module, prefix))
|
||||
|
||||
def get_module_name(doctype, module, prefix=""):
|
||||
from frappe.modules import scrub
|
||||
return '{app}.{module}.doctype.{doctype}.{prefix}{doctype}'.format(\
|
||||
app = scrub(frappe.local.module_app[scrub(module)]),
|
||||
module = scrub(module), doctype = scrub(doctype), prefix=prefix)
|
||||
|
||||
|
||||
def run_server_obj(server_obj, method_name, arg=None):
|
||||
"""
|
||||
Executes a method (`method_name`) from the given object (`server_obj`)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ class DatabaseQuery(object):
|
|||
def __init__(self, doctype):
|
||||
self.doctype = doctype
|
||||
self.tables = []
|
||||
self.meta = []
|
||||
self.conditions = []
|
||||
self.ignore_permissions = False
|
||||
self.fields = ["name"]
|
||||
|
|
@ -53,7 +52,6 @@ class DatabaseQuery(object):
|
|||
def prepare_args(self):
|
||||
self.parse_args()
|
||||
self.extract_tables()
|
||||
self.load_metadata()
|
||||
self.remove_user_tags()
|
||||
self.build_conditions()
|
||||
|
||||
|
|
@ -111,20 +109,13 @@ class DatabaseQuery(object):
|
|||
if not table_name[0]=='`':
|
||||
table_name = '`' + table_name + '`'
|
||||
if not table_name in self.tables:
|
||||
self.tables.append(table_name)
|
||||
self.append_table(table_name)
|
||||
|
||||
def load_metadata(self):
|
||||
"""load all doctypes and roles"""
|
||||
self.meta = {}
|
||||
|
||||
for t in self.tables:
|
||||
if t.startswith('`'):
|
||||
doctype = t[4:-1]
|
||||
if self.meta.get(doctype):
|
||||
continue
|
||||
if (not self.ignore_permissions) and (not frappe.has_permission(doctype)):
|
||||
raise frappe.PermissionError, doctype
|
||||
self.meta[doctype] = frappe.model.doctype.get(doctype)
|
||||
def append_table(self, table_name):
|
||||
self.tables.append(table_name)
|
||||
doctype = table_name[4:-1]
|
||||
if (not self.ignore_permissions) and (not frappe.has_permission(doctype)):
|
||||
raise frappe.PermissionError, doctype
|
||||
|
||||
def remove_user_tags(self):
|
||||
"""remove column _user_tags if not in table"""
|
||||
|
|
@ -170,10 +161,7 @@ class DatabaseQuery(object):
|
|||
|
||||
tname = ('`tab' + f[0] + '`')
|
||||
if not tname in self.tables:
|
||||
self.tables.append(tname)
|
||||
|
||||
if not tname in self.meta:
|
||||
self.load_metadata()
|
||||
self.append_table(tname)
|
||||
|
||||
# prepare in condition
|
||||
if f[2] in ['in', 'not in']:
|
||||
|
|
@ -184,7 +172,7 @@ class DatabaseQuery(object):
|
|||
f[3] = "(" + ', '.join(opts) + ")"
|
||||
self.conditions.append('ifnull(' + tname + '.' + f[1] + ", '') " + f[2] + " " + f[3])
|
||||
else:
|
||||
df = self.meta[f[0]].get({"doctype": "DocField", "fieldname": f[1]})
|
||||
df = frappe.get_meta(f[0]).get("fields", {"fieldname": f[1]})
|
||||
|
||||
if f[2] == "like" or (isinstance(f[3], basestring) and
|
||||
(not df or df[0].fieldtype not in ["Float", "Int", "Currency", "Percent"])):
|
||||
|
|
@ -216,10 +204,9 @@ class DatabaseQuery(object):
|
|||
self.or_conditions = []
|
||||
|
||||
if not self.tables: self.extract_tables()
|
||||
if not self.meta: self.load_metadata()
|
||||
|
||||
# explict permissions
|
||||
restricted_by_user = frappe.permissions.get_user_perms(self.meta[self.doctype]).restricted
|
||||
restricted_by_user = frappe.permissions.get_user_perms(frappe.get_meta(self.doctype)).restricted
|
||||
|
||||
# get restrictions
|
||||
restrictions = frappe.defaults.get_restrictions()
|
||||
|
|
@ -238,7 +225,7 @@ class DatabaseQuery(object):
|
|||
return self.match_filters
|
||||
|
||||
def add_restrictions(self, restrictions):
|
||||
fields_to_check = self.meta[self.doctype].get_restricted_fields(restrictions.keys())
|
||||
fields_to_check = frappe.get_meta(self.doctype).get_restricted_fields(restrictions.keys())
|
||||
if self.doctype in restrictions:
|
||||
fields_to_check.append(frappe._dict({"fieldname":"name", "options":self.doctype}))
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ type_map = {
|
|||
,'Code': ('text', '')
|
||||
,'Text Editor': ('text', '')
|
||||
,'Date': ('date', '')
|
||||
,'Datetime': ('datetime', '')
|
||||
,'Datetime': ('datetime', '6')
|
||||
,'Time': ('time', '')
|
||||
,'Text': ('text', '')
|
||||
,'Data': ('varchar', '255')
|
||||
|
|
@ -78,8 +78,8 @@ class DbTable:
|
|||
# create table
|
||||
frappe.db.sql("""create table `%s` (
|
||||
name varchar(255) not null primary key,
|
||||
creation datetime,
|
||||
modified datetime,
|
||||
creation datetime(6),
|
||||
modified datetime(6),
|
||||
modified_by varchar(40),
|
||||
owner varchar(60),
|
||||
docstatus int(1) default '0',
|
||||
|
|
|
|||
|
|
@ -5,196 +5,27 @@ from __future__ import unicode_literals
|
|||
import frappe
|
||||
from frappe import _, msgprint
|
||||
from frappe.utils import cint, flt, cstr, now
|
||||
from frappe.model import default_fields
|
||||
from frappe.model.db_schema import type_map
|
||||
from frappe.model.naming import set_new_name
|
||||
from frappe.modules import load_doctype_module
|
||||
from frappe.model.base_document import BaseDocument
|
||||
|
||||
# save / update
|
||||
# once_only validation
|
||||
# permissions
|
||||
# methods
|
||||
# timestamps and docstatus
|
||||
|
||||
class BaseDocument(object):
|
||||
def __init__(self, d, valid_columns=None):
|
||||
self.update(d, valid_columns=valid_columns)
|
||||
|
||||
def __getattr__(self, key):
|
||||
if self.__dict__.has_key(key):
|
||||
return self.__dict__[key]
|
||||
|
||||
if key!= "_valid_columns" and key in self.get_valid_columns():
|
||||
return None
|
||||
|
||||
raise AttributeError(key)
|
||||
|
||||
def update(self, d, valid_columns=None):
|
||||
if valid_columns:
|
||||
self.__dict__["_valid_columns"] = valid_columns
|
||||
if "doctype" in d:
|
||||
self.set("doctype", d.get("doctype"))
|
||||
for key, value in d.iteritems():
|
||||
self.set(key, value)
|
||||
def get_doc(arg1, arg2=None):
|
||||
if isinstance(arg1, basestring):
|
||||
doctype = arg1
|
||||
else:
|
||||
doctype = arg1.get("doctype")
|
||||
|
||||
def get(self, key=None, filters=None, limit=None, default=None):
|
||||
if key:
|
||||
if filters:
|
||||
return _filter(self.__dict__.get(key), filters, limit=limit)
|
||||
else:
|
||||
return self.__dict__.get(key, default)
|
||||
else:
|
||||
return self.__dict__
|
||||
|
||||
def set(self, key, value, valid_columns=None):
|
||||
if isinstance(value, list):
|
||||
tmp = []
|
||||
for v in value:
|
||||
tmp.append(self._init_child(v, key, valid_columns))
|
||||
value = tmp
|
||||
|
||||
self.__dict__[key] = value
|
||||
|
||||
def append(self, key, value):
|
||||
if isinstance(value, dict):
|
||||
if not self.get(key):
|
||||
self.__dict__[key] = []
|
||||
self.get(key).append(self._init_child(value, key))
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
def extend(self, key, value):
|
||||
if isinstance(value, list):
|
||||
for v in value:
|
||||
self.append(v)
|
||||
else:
|
||||
raise ValueError
|
||||
module = load_doctype_module(doctype)
|
||||
classname = doctype.replace(" ", "")
|
||||
if hasattr(module, classname):
|
||||
_class = getattr(module, classname)
|
||||
if issubclass(_class, Document):
|
||||
return getattr(module, classname)(arg1, arg2)
|
||||
|
||||
def _init_child(self, value, key, valid_columns=None):
|
||||
if not self.doctype:
|
||||
return value
|
||||
if not isinstance(value, BaseDocument):
|
||||
if not value.get("doctype"):
|
||||
value["doctype"] = self.get_table_field_doctype(key)
|
||||
if not value.get("doctype"):
|
||||
raise AttributeError, key
|
||||
value = BaseDocument(value, valid_columns=valid_columns)
|
||||
|
||||
value.parent = self.name
|
||||
value.parenttype = self.doctype
|
||||
value.parentfield = key
|
||||
if not value.idx:
|
||||
value.idx = len(self.get(key) or []) + 1
|
||||
|
||||
return value
|
||||
return Document(arg1, arg2)
|
||||
|
||||
@property
|
||||
def meta(self):
|
||||
if not self.get("_meta"):
|
||||
self._meta = frappe.get_meta(self.doctype)
|
||||
return self._meta
|
||||
|
||||
def get_valid_dict(self):
|
||||
d = {}
|
||||
for fieldname in self.valid_columns:
|
||||
d[fieldname] = self.get(fieldname)
|
||||
return d
|
||||
|
||||
@property
|
||||
def valid_columns(self):
|
||||
return self.get_valid_columns()
|
||||
|
||||
def get_valid_columns(self):
|
||||
if not hasattr(self, "_valid_columns"):
|
||||
doctype = self.__dict__.get("doctype")
|
||||
self._valid_columns = default_fields[1:] + \
|
||||
[df.fieldname for df in frappe.get_meta(doctype).get("fields")
|
||||
if df.fieldtype in type_map]
|
||||
|
||||
return self._valid_columns
|
||||
|
||||
def get_table_field_doctype(self, fieldname):
|
||||
return self.meta.get("fields", {"fieldname":fieldname})[0].options
|
||||
|
||||
def db_insert(self):
|
||||
set_new_name(self)
|
||||
d = self.get_valid_dict()
|
||||
columns = d.keys()
|
||||
frappe.db.sql("""insert into `tab{doctype}`
|
||||
({columns}) values ({values})""".format(
|
||||
doctype = self.doctype,
|
||||
columns = ", ".join(["`"+c+"`" for c in columns]),
|
||||
values = ", ".join(["%s"] * len(columns))
|
||||
), d.values())
|
||||
self.set("__islocal", False)
|
||||
|
||||
def db_update(self):
|
||||
d = self.get_valid_dict()
|
||||
columns = d.keys()
|
||||
frappe.db.sql("""update `tab{doctype}`
|
||||
set {values} where name=%s""".format(
|
||||
doctype = self.doctype,
|
||||
values = ", ".join(["`"+c+"`=%s" for c in columns])
|
||||
), d.values() + [d.get("name")])
|
||||
|
||||
def fix_numeric_types(self):
|
||||
for df in self.meta.get("fields"):
|
||||
if df.fieldtype in ("Int", "Check"):
|
||||
self.set(df.fieldname, cint(self.get(df.fieldname)))
|
||||
elif df.fieldtype in ("Float", "Currency"):
|
||||
self.set(df.fieldname, flt(self.get(df.fieldname)))
|
||||
|
||||
if self.docstatus is not None:
|
||||
self.docstatus = cint(self.docstatus)
|
||||
|
||||
def set_missing_values(self, d):
|
||||
for key, value in d.iteritems():
|
||||
if self.get(key) is None:
|
||||
self.set(key, value)
|
||||
|
||||
def get_missing_mandatory_fields(self):
|
||||
"""Get mandatory fields that do not have any values"""
|
||||
def get_msg(df):
|
||||
if df.fieldtype == "Table":
|
||||
return "{}: {}: {}".format(_("Error"), _("Data missing in table"), _(df.label))
|
||||
|
||||
elif self.parentfield:
|
||||
return "{}: {} #{}: {}: {}".format(_("Error"), _("Row"), self.idx,
|
||||
_("Value missing for"), _(df.label))
|
||||
|
||||
else:
|
||||
return "{}: {}: {}".format(_("Error"), _("Value missing for"), _(df.label))
|
||||
|
||||
missing = []
|
||||
|
||||
for df in self.meta.get("fields", {"reqd": 1}):
|
||||
if self.get(df.fieldname) in (None, []):
|
||||
missing.append((df.fieldname, get_msg(df)))
|
||||
|
||||
return missing
|
||||
|
||||
def get_invalid_links(self):
|
||||
def get_msg(df, docname):
|
||||
if self.parentfield:
|
||||
return "{} #{}: {}: {}".format(_("Row"), self.idx, _(df.label), docname)
|
||||
else:
|
||||
return "{}: {}".format(_(df.label), docname)
|
||||
|
||||
invalid_links = []
|
||||
for df in self.meta.get_link_fields():
|
||||
doctype = df.options
|
||||
|
||||
if not doctype:
|
||||
frappe.throw("Options not set for link field: {}".format(df.fieldname))
|
||||
|
||||
elif doctype.lower().startswith("link:"):
|
||||
doctype = doctype[5:]
|
||||
|
||||
docname = self.get(df.fieldname)
|
||||
if docname and not frappe.db.get_value(doctype, docname):
|
||||
invalid_links.append((df.fieldname, docname, get_msg(df, docname)))
|
||||
|
||||
return invalid_links
|
||||
|
||||
class Document(BaseDocument):
|
||||
def __init__(self, arg1, arg2=None):
|
||||
self.doctype = self.name = None
|
||||
|
|
@ -224,7 +55,7 @@ class Document(BaseDocument):
|
|||
def load_from_db(self):
|
||||
if not getattr(self, "_metaclass", False) and self.meta.issingle:
|
||||
self.update(frappe.db.get_singles_dict(self.doctype))
|
||||
self.fix_numeric_types()
|
||||
self._fix_numeric_types()
|
||||
|
||||
else:
|
||||
d = frappe.db.get_value(self.doctype, self.name, "*", as_dict=1)
|
||||
|
|
@ -241,13 +72,24 @@ class Document(BaseDocument):
|
|||
|
||||
def get_table_fields(self):
|
||||
return self.meta.get('fields', {"fieldtype":"Table"})
|
||||
|
||||
def has_permission(self, permtype):
|
||||
if getattr(self, "_ignore_permissions", False):
|
||||
return True
|
||||
return frappe.has_permission(self.doctype, permtype, self)
|
||||
|
||||
def insert(self):
|
||||
# check links
|
||||
# check permissions
|
||||
self.set("__islocal", True)
|
||||
|
||||
if not self.has_permission("create"):
|
||||
raise frappe.PermissionError
|
||||
self._set_defaults()
|
||||
self._set_docstatus_user_and_timestamp()
|
||||
self._check_if_latest()
|
||||
self.run_method("before_insert")
|
||||
self.run_before_save_methods()
|
||||
self._validate()
|
||||
|
||||
# run validate, on update etc.
|
||||
|
|
@ -262,13 +104,20 @@ class Document(BaseDocument):
|
|||
for d in self.get_all_children():
|
||||
d.parent = self.name
|
||||
d.db_insert()
|
||||
self.run_method("after_insert")
|
||||
self.run_post_save_methods()
|
||||
|
||||
def save(self):
|
||||
if self.get("__islocal") or not self.get("name"):
|
||||
self.insert()
|
||||
return
|
||||
|
||||
if not self.has_permission("write"):
|
||||
raise frappe.PermissionError
|
||||
|
||||
self._set_docstatus_user_and_timestamp()
|
||||
self._check_if_latest()
|
||||
self.run_before_save_methods()
|
||||
self._validate()
|
||||
|
||||
# parent
|
||||
|
|
@ -278,10 +127,14 @@ class Document(BaseDocument):
|
|||
self.db_update()
|
||||
|
||||
# children
|
||||
ignore_children_type = self.get("_ignore_children_type", [])
|
||||
for d in self.get_all_children():
|
||||
d.parent = self.name
|
||||
d.db_update()
|
||||
|
||||
if d.doctype not in _ignore_children_type:
|
||||
d.parent = self.name
|
||||
d.db_update()
|
||||
|
||||
self.run_post_save_methods()
|
||||
|
||||
def update_single(self, d):
|
||||
frappe.db.sql("""delete from tabSingles where doctype=%s""", self.doctype)
|
||||
for field, value in d.iteritems():
|
||||
|
|
@ -308,6 +161,14 @@ class Document(BaseDocument):
|
|||
d.owner = self.owner
|
||||
if not d.creation:
|
||||
d.creation = self.creation
|
||||
|
||||
def _validate(self):
|
||||
self._validate_mandatory()
|
||||
self._validate_links()
|
||||
self._validate_constants()
|
||||
for d in self.get_all_children():
|
||||
d._validate_constants()
|
||||
self._extract_images_from_text_editor()
|
||||
|
||||
def _set_defaults(self):
|
||||
if frappe.flags.in_import:
|
||||
|
|
@ -323,16 +184,10 @@ class Document(BaseDocument):
|
|||
if isinstance(value, list):
|
||||
for d in value:
|
||||
d.set_missing_values(new_doc)
|
||||
|
||||
def _validate(self):
|
||||
self.check_if_latest()
|
||||
self.validate_mandatory()
|
||||
self.validate_links()
|
||||
|
||||
# check restrictions
|
||||
|
||||
def check_if_latest(self):
|
||||
|
||||
def _check_if_latest(self):
|
||||
conflict = False
|
||||
self._action = "save"
|
||||
if not self.get('__islocal'):
|
||||
if self.meta.issingle:
|
||||
modified = frappe.db.get_value(self.doctype, self.name, "modified")
|
||||
|
|
@ -366,6 +221,8 @@ class Document(BaseDocument):
|
|||
self._action = "save"
|
||||
elif self.docstatus==1:
|
||||
self._action = "submit"
|
||||
if not self.has_permission("submit"):
|
||||
raise frappe.PermissionError
|
||||
else:
|
||||
raise frappe.DocstatusTransitionError
|
||||
|
||||
|
|
@ -373,8 +230,12 @@ class Document(BaseDocument):
|
|||
if self.docstatus==1:
|
||||
self._action = "update_after_submit"
|
||||
self.validate_update_after_submit()
|
||||
if not self.has_permission("submit"):
|
||||
raise frappe.PermissionError
|
||||
elif self.docstatus==2:
|
||||
self._action = "cancel"
|
||||
if not self.has_permission("cancel"):
|
||||
raise frappe.PermissionError
|
||||
else:
|
||||
raise frappe.DocstatusTransitionError
|
||||
|
||||
|
|
@ -385,13 +246,13 @@ class Document(BaseDocument):
|
|||
# check only allowed values are updated
|
||||
pass
|
||||
|
||||
def validate_mandatory(self):
|
||||
def _validate_mandatory(self):
|
||||
if self.get("ignore_mandatory"):
|
||||
return
|
||||
|
||||
missing = self.get_missing_mandatory_fields()
|
||||
missing = self._get_missing_mandatory_fields()
|
||||
for d in self.get_all_children():
|
||||
missing.extend(d.get_missing_mandatory_fields())
|
||||
missing.extend(d._get_missing_mandatory_fields())
|
||||
|
||||
if not missing:
|
||||
return
|
||||
|
|
@ -401,7 +262,7 @@ class Document(BaseDocument):
|
|||
|
||||
raise frappe.MandatoryError(", ".join((each[0] for each in missing)))
|
||||
|
||||
def validate_links(self):
|
||||
def _validate_links(self):
|
||||
if self.get("ignore_links"):
|
||||
return
|
||||
|
||||
|
|
@ -423,40 +284,51 @@ class Document(BaseDocument):
|
|||
if isinstance(value, list):
|
||||
ret.extend(value)
|
||||
return ret
|
||||
|
||||
def _extract_images_from_text_editor(self):
|
||||
from frappe.utils.file_manager import extract_images_from_html
|
||||
if self.doctype != "DocType":
|
||||
for df in self.meta.get("fields", {"fieldtype":"Text Editor"}):
|
||||
extract_images_from_html(self, df.fieldname)
|
||||
|
||||
def trigger(self, func, *args, **kwargs):
|
||||
return
|
||||
def run_method(self, method, *args, **kwargs):
|
||||
"""run standard triggers, plus those in frappe"""
|
||||
def add_to_response(out, new_response):
|
||||
if isinstance(new_response, dict):
|
||||
out.update(new_response)
|
||||
|
||||
if hasattr(self, method):
|
||||
add_to_response(frappe.local.response,
|
||||
getattr(self, method)(*args, **kwargs))
|
||||
|
||||
args = [self, method] + list(args or [])
|
||||
|
||||
for handler in frappe.get_hooks("bean_event:" + self.doctype + ":" + method) \
|
||||
+ frappe.get_hooks("bean_event:*:" + method):
|
||||
add_to_response(frappe.local.response,
|
||||
frappe.call(frappe.get_attr(handler), *args, **kwargs))
|
||||
|
||||
def _filter(data, filters, limit=None):
|
||||
"""pass filters as:
|
||||
{"key": "val", "key": ["!=", "val"],
|
||||
"key": ["in", "val"], "key": ["not in", "val"], "key": "^val",
|
||||
"key" : True (exists), "key": False (does not exist) }"""
|
||||
return frappe.local.response
|
||||
|
||||
out = []
|
||||
|
||||
for d in data:
|
||||
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
|
||||
def run_before_save_methods(self):
|
||||
if self._action=="save":
|
||||
self.run_method("validate")
|
||||
self.run_method("before_save")
|
||||
elif self._action=="submit":
|
||||
self.run_method("validate")
|
||||
self.run_method("before_submit")
|
||||
elif self._action=="cancel":
|
||||
self.run_method("before_cancel")
|
||||
elif self._action=="update_after_submit":
|
||||
self.run_method("before_update_after_submit")
|
||||
|
||||
if add:
|
||||
out.append(d)
|
||||
if limit and (len(out)-1)==limit:
|
||||
break
|
||||
|
||||
return out
|
||||
def run_post_save_methods(self):
|
||||
if self._action=="save":
|
||||
self.run_method("on_update")
|
||||
elif self._action=="submit":
|
||||
self.run_method("on_update")
|
||||
self.run_method("on_submit")
|
||||
elif self._action=="cancel":
|
||||
self.run_method("on_cancel")
|
||||
elif self._action=="update_after_submit":
|
||||
self.run_method("on_update_after_submit")
|
||||
|
|
|
|||
|
|
@ -115,6 +115,20 @@ class Meta(Document):
|
|||
idx += 1
|
||||
|
||||
self.set("fields", newlist)
|
||||
|
||||
def get_restricted_fields(self, restricted_types):
|
||||
restricted_fields = self.get("fields", {
|
||||
"fieldtype":"Link",
|
||||
"parent": self.name,
|
||||
"ignore_restrictions":("!=", 1),
|
||||
"options":("in", restricted_types)
|
||||
})
|
||||
if self.name in restricted_types:
|
||||
restricted_fields.append(frappe._dict({
|
||||
"label":"Name", "fieldname":"name", "options": self.name
|
||||
}))
|
||||
return restricted_fields
|
||||
|
||||
|
||||
#######
|
||||
|
||||
|
|
|
|||
|
|
@ -44,3 +44,14 @@ def export_doc(doctype, name, module=None):
|
|||
|
||||
def get_doctype_module(doctype):
|
||||
return frappe.db.get_value('DocType', doctype, 'module') or "core"
|
||||
|
||||
def load_doctype_module(doctype, module=None, prefix=""):
|
||||
if not module:
|
||||
module = get_doctype_module(doctype)
|
||||
return frappe.get_module(get_module_name(doctype, module, prefix))
|
||||
|
||||
def get_module_name(doctype, module, prefix=""):
|
||||
from frappe.modules import scrub
|
||||
return '{app}.{module}.doctype.{doctype}.{prefix}{doctype}'.format(\
|
||||
app = scrub(frappe.local.module_app[scrub(module)]),
|
||||
module = scrub(module), doctype = scrub(doctype), prefix=prefix)
|
||||
|
|
|
|||
|
|
@ -22,3 +22,4 @@ frappe.patches.4_0.private_backups
|
|||
frappe.patches.4_0.set_module_in_report
|
||||
frappe.patches.4_0.remove_old_parent
|
||||
frappe.patches.4_0.rename_profile_to_user
|
||||
frappe.patches.4_0.update_datetime
|
||||
|
|
|
|||
9
frappe/patches/4_0/update_datetime.py
Normal file
9
frappe/patches/4_0/update_datetime.py
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import frappe
|
||||
|
||||
def execute():
|
||||
for table in frappe.db.sql_list("show tables"):
|
||||
for field in frappe.db.sql("desc `%s`" % table):
|
||||
if field[1]=="datetime":
|
||||
frappe.db.sql("alter table `%s` change `%s` `%s` datetime(6)" % \
|
||||
(table, field[0], field[0]))
|
||||
|
||||
|
|
@ -19,12 +19,12 @@ def has_permission(doctype, ptype="read", refdoc=None, verbose=True):
|
|||
if frappe.is_table(doctype):
|
||||
return True
|
||||
|
||||
meta = frappe.get_doctype(doctype)
|
||||
meta = frappe.get_meta(doctype)
|
||||
|
||||
if ptype=="submit" and not cint(meta[0].is_submittable):
|
||||
if ptype=="submit" and not cint(meta.is_submittable):
|
||||
return False
|
||||
|
||||
if ptype=="import" and not cint(meta[0].allow_import):
|
||||
if ptype=="import" and not cint(meta.allow_import):
|
||||
return False
|
||||
|
||||
if frappe.session.user=="Administrator":
|
||||
|
|
@ -36,24 +36,26 @@ def has_permission(doctype, ptype="read", refdoc=None, verbose=True):
|
|||
|
||||
if refdoc:
|
||||
if isinstance(refdoc, basestring):
|
||||
refdoc = frappe.doc(meta[0].name, refdoc)
|
||||
refdoc = frappe.doc(meta.name, refdoc)
|
||||
|
||||
if not has_unrestricted_access(meta, refdoc, verbose=verbose):
|
||||
return False
|
||||
|
||||
if not has_additional_permission(refdoc):
|
||||
if not has_controller_permissions(refdoc):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def get_user_perms(meta, user=None):
|
||||
cache_key = (meta[0].name, user)
|
||||
if not user:
|
||||
user = frappe.session.user
|
||||
cache_key = (meta.name, user)
|
||||
if not frappe.local.user_perms.get(cache_key):
|
||||
perms = frappe._dict()
|
||||
user_roles = frappe.get_roles(user)
|
||||
|
||||
for p in meta.get({"doctype": "DocPerm"}):
|
||||
if cint(p.permlevel)==0 and (p.role=="All" or p.role in user_roles):
|
||||
for p in meta.permissions:
|
||||
if cint(p.permlevel)==0 and (p.role in user_roles):
|
||||
for ptype in rights:
|
||||
if ptype == "restricted":
|
||||
perms[ptype] = perms.get(ptype, 1) and cint(p.get(ptype))
|
||||
|
|
@ -100,8 +102,8 @@ def has_unrestricted_access(meta, refdoc, verbose=True):
|
|||
# check all restrictions before returning
|
||||
return False if has_restricted_data else True
|
||||
|
||||
def has_additional_permission(doc):
|
||||
if doc.fields.get("__islocal"):
|
||||
def has_controller_permissions(doc):
|
||||
if doc.get("__islocal"):
|
||||
bean = frappe.bean([doc])
|
||||
else:
|
||||
bean = frappe.bean(doc.doctype, doc.name)
|
||||
|
|
|
|||
|
|
@ -3,11 +3,9 @@
|
|||
|
||||
import frappe, unittest, time
|
||||
|
||||
from frappe.model.document import Document
|
||||
|
||||
class TestDocument(unittest.TestCase):
|
||||
def test_load(self):
|
||||
d = Document("DocType", "User")
|
||||
d = frappe.get_doc("DocType", "User")
|
||||
self.assertEquals(d.doctype, "DocType")
|
||||
self.assertEquals(d.name, "User")
|
||||
self.assertEquals(d.allow_rename, 1)
|
||||
|
|
@ -16,13 +14,13 @@ class TestDocument(unittest.TestCase):
|
|||
self.assertTrue(filter(lambda d: d.fieldname=="email", d.fields))
|
||||
|
||||
def test_load_single(self):
|
||||
d = Document("Website Settings", "Website Settings")
|
||||
d = frappe.get_doc("Website Settings", "Website Settings")
|
||||
self.assertEquals(d.name, "Website Settings")
|
||||
self.assertEquals(d.doctype, "Website Settings")
|
||||
self.assertTrue(d.disable_signup in (0, 1))
|
||||
|
||||
def test_insert(self):
|
||||
d = Document({
|
||||
d = frappe.get_doc({
|
||||
"doctype":"Event",
|
||||
"subject":"_Test Event 1",
|
||||
"starts_on": "2014-01-01",
|
||||
|
|
@ -38,7 +36,7 @@ class TestDocument(unittest.TestCase):
|
|||
return d
|
||||
|
||||
def test_insert_with_child(self):
|
||||
d = Document({
|
||||
d = frappe.get_doc({
|
||||
"doctype":"Event",
|
||||
"subject":"_Test Event 2",
|
||||
"starts_on": "2014-01-01",
|
||||
|
|
@ -54,7 +52,7 @@ class TestDocument(unittest.TestCase):
|
|||
self.assertEquals(frappe.db.get_value("Event", d.name, "subject"),
|
||||
"_Test Event 2")
|
||||
|
||||
d1 = Document("Event", d.name)
|
||||
d1 = frappe.get_doc("Event", d.name)
|
||||
self.assertTrue(d1.event_individuals[0].person, "Administrator")
|
||||
|
||||
def test_update(self):
|
||||
|
|
@ -65,7 +63,7 @@ class TestDocument(unittest.TestCase):
|
|||
self.assertEquals(frappe.db.get_value(d.doctype, d.name, "subject"), "subject changed")
|
||||
|
||||
def test_mandatory(self):
|
||||
d = Document({
|
||||
d = frappe.get_doc({
|
||||
"doctype": "User",
|
||||
"email": "test_mandatory@example.com",
|
||||
})
|
||||
|
|
@ -77,20 +75,29 @@ class TestDocument(unittest.TestCase):
|
|||
|
||||
def test_confict_validation(self):
|
||||
d1 = self.test_insert()
|
||||
d2 = Document(d1.doctype, d1.name)
|
||||
time.sleep(1)
|
||||
d2 = frappe.get_doc(d1.doctype, d1.name)
|
||||
d1.save()
|
||||
self.assertRaises(frappe.TimestampMismatchError, d2.save)
|
||||
|
||||
def test_confict_validation_single(self):
|
||||
d1 = Document("Website Settings", "Website Settings")
|
||||
d2 = Document("Website Settings", "Website Settings")
|
||||
time.sleep(1)
|
||||
d1 = frappe.get_doc("Website Settings", "Website Settings")
|
||||
d2 = frappe.get_doc("Website Settings", "Website Settings")
|
||||
d1.save()
|
||||
self.assertRaises(frappe.TimestampMismatchError, d2.save)
|
||||
|
||||
def test_permission(self):
|
||||
frappe.set_user("Guest")
|
||||
d = self.assertRaises(frappe.PermissionError, self.test_insert)
|
||||
frappe.set_user("Administrator")
|
||||
|
||||
def test_permission_single(self):
|
||||
frappe.set_user("Guest")
|
||||
d = frappe.get_doc("Website Settings", "Website Settigns")
|
||||
self.assertRaises(frappe.PermissionError, d.save)
|
||||
frappe.set_user("Administrator")
|
||||
|
||||
def test_link_validation(self):
|
||||
d = Document({
|
||||
d = frappe.get_doc({
|
||||
"doctype": "User",
|
||||
"email": "test_link_validation@example.com",
|
||||
"first_name": "Link Validation",
|
||||
|
|
@ -106,4 +113,12 @@ class TestDocument(unittest.TestCase):
|
|||
"role": "System Manager"
|
||||
})
|
||||
d.insert()
|
||||
self.assertEquals(frappe.db.get_value("User", d.name), d.name)
|
||||
self.assertEquals(frappe.db.get_value("User", d.name), d.name)
|
||||
|
||||
def test_validate(self):
|
||||
d = self.test_insert()
|
||||
d.starts_on = "2014-01-01"
|
||||
d.ends_on = "2013-01-01"
|
||||
self.assertRaises(frappe.ValidationError, d.validate)
|
||||
self.assertRaises(frappe.ValidationError, d.save)
|
||||
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ def now():
|
|||
return getdate(frappe.local.current_date).strftime("%Y-%m-%d") + " " + \
|
||||
now_datetime().strftime('%H:%M:%S')
|
||||
else:
|
||||
return now_datetime().strftime('%Y-%m-%d %H:%M:%S')
|
||||
return now_datetime().strftime('%Y-%m-%d %H:%M:%S.%f')
|
||||
|
||||
def nowdate():
|
||||
"""return current date as yyyy-mm-dd"""
|
||||
|
|
@ -217,7 +217,7 @@ def get_datetime(datetime_str):
|
|||
if isinstance(datetime_str, datetime):
|
||||
return datetime_str.replace(microsecond=0, tzinfo=None)
|
||||
|
||||
return datetime.strptime(datetime_str, '%Y-%m-%d %H:%M:%S')
|
||||
return datetime.strptime(datetime_str, '%Y-%m-%d %H:%M:%S.%f')
|
||||
|
||||
def get_datetime_str(datetime_obj):
|
||||
if isinstance(datetime_obj, basestring):
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue