Rushabh Mehta пре 11 година
родитељ
комит
893588a0ce
14 измењених фајлова са 450 додато и 299 уклоњено
  1. +4
    -0
      frappe/__init__.py
  2. +6
    -0
      frappe/core/doctype/event/event.py
  3. +242
    -0
      frappe/model/base_document.py
  4. +1
    -13
      frappe/model/code.py
  5. +10
    -23
      frappe/model/db_query.py
  6. +3
    -3
      frappe/model/db_schema.py
  7. +105
    -233
      frappe/model/document.py
  8. +14
    -0
      frappe/model/meta.py
  9. +11
    -0
      frappe/modules/__init__.py
  10. +1
    -0
      frappe/patches.txt
  11. +9
    -0
      frappe/patches/4_0/update_datetime.py
  12. +12
    -10
      frappe/permissions.py
  13. +30
    -15
      frappe/tests/test_document.py
  14. +2
    -2
      frappe/utils/__init__.py

+ 4
- 0
frappe/__init__.py Прегледај датотеку

@@ -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
- 0
frappe/core/doctype/event/event.py Прегледај датотеку

@@ -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
- 0
frappe/model/base_document.py Прегледај датотеку

@@ -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

+ 1
- 13
frappe/model/code.py Прегледај датотеку

@@ -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`)


+ 10
- 23
frappe/model/db_query.py Прегледај датотеку

@@ -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}))


+ 3
- 3
frappe/model/db_schema.py Прегледај датотеку

@@ -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',


+ 105
- 233
frappe/model/document.py Прегледај датотеку

@@ -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")

+ 14
- 0
frappe/model/meta.py Прегледај датотеку

@@ -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


#######



+ 11
- 0
frappe/modules/__init__.py Прегледај датотеку

@@ -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)

+ 1
- 0
frappe/patches.txt Прегледај датотеку

@@ -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
- 0
frappe/patches/4_0/update_datetime.py Прегледај датотеку

@@ -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]))

+ 12
- 10
frappe/permissions.py Прегледај датотеку

@@ -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)


+ 30
- 15
frappe/tests/test_document.py Прегледај датотеку

@@ -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)

+ 2
- 2
frappe/utils/__init__.py Прегледај датотеку

@@ -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…
Откажи
Сачувај