Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 
 
 

501 строка
15 KiB

  1. # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
  2. # MIT License. See license.txt
  3. from __future__ import unicode_literals
  4. import frappe, json, sys
  5. from frappe import _
  6. from frappe.utils import cint, flt, now, cstr, strip_html
  7. from frappe.model import default_fields
  8. from frappe.model.naming import set_new_name
  9. from frappe.modules import load_doctype_module
  10. _classes = {}
  11. def get_controller(doctype):
  12. """Returns the **class** object of the given DocType.
  13. For `custom` type, returns `frappe.model.document.Document`.
  14. :param doctype: DocType name as string."""
  15. from frappe.model.document import Document
  16. if not doctype in _classes:
  17. module_name, custom = frappe.db.get_value("DocType", doctype, ["module", "custom"]) \
  18. or ["Core", False]
  19. if custom:
  20. _class = Document
  21. else:
  22. module = load_doctype_module(doctype, module_name)
  23. classname = doctype.replace(" ", "").replace("-", "")
  24. if hasattr(module, classname):
  25. _class = getattr(module, classname)
  26. if issubclass(_class, BaseDocument):
  27. _class = getattr(module, classname)
  28. else:
  29. raise ImportError, doctype
  30. else:
  31. raise ImportError, doctype
  32. _classes[doctype] = _class
  33. return _classes[doctype]
  34. class BaseDocument(object):
  35. ignore_in_getter = ("doctype", "_meta", "meta", "_table_fields", "_valid_columns")
  36. def __init__(self, d):
  37. self.update(d)
  38. self.dont_update_if_missing = []
  39. if hasattr(self, "__setup__"):
  40. self.__setup__()
  41. @property
  42. def meta(self):
  43. if not hasattr(self, "_meta"):
  44. self._meta = frappe.get_meta(self.doctype)
  45. return self._meta
  46. def update(self, d):
  47. if "doctype" in d:
  48. self.set("doctype", d.get("doctype"))
  49. # first set default field values of base document
  50. for key in default_fields:
  51. if key in d:
  52. self.set(key, d.get(key))
  53. for key, value in d.iteritems():
  54. self.set(key, value)
  55. return self
  56. def update_if_missing(self, d):
  57. if isinstance(d, BaseDocument):
  58. d = d.get_valid_dict()
  59. if "doctype" in d:
  60. self.set("doctype", d.get("doctype"))
  61. for key, value in d.iteritems():
  62. # dont_update_if_missing is a list of fieldnames, for which, you don't want to set default value
  63. if (self.get(key) is None) and (value is not None) and (key not in self.dont_update_if_missing):
  64. self.set(key, value)
  65. def get_db_value(self, key):
  66. return frappe.db.get_value(self.doctype, self.name, key)
  67. def get(self, key=None, filters=None, limit=None, default=None):
  68. if key:
  69. if isinstance(key, dict):
  70. return _filter(self.get_all_children(), key, limit=limit)
  71. if filters:
  72. if isinstance(filters, dict):
  73. value = _filter(self.__dict__.get(key, []), filters, limit=limit)
  74. else:
  75. default = filters
  76. filters = None
  77. value = self.__dict__.get(key, default)
  78. else:
  79. value = self.__dict__.get(key, default)
  80. if value is None and key not in self.ignore_in_getter \
  81. and key in (d.fieldname for d in self.meta.get_table_fields()):
  82. self.set(key, [])
  83. value = self.__dict__.get(key)
  84. return value
  85. else:
  86. return self.__dict__
  87. def getone(self, key, filters=None):
  88. return self.get(key, filters=filters, limit=1)[0]
  89. def set(self, key, value, as_value=False):
  90. if isinstance(value, list) and not as_value:
  91. self.__dict__[key] = []
  92. self.extend(key, value)
  93. else:
  94. self.__dict__[key] = value
  95. def delete_key(self, key):
  96. if key in self.__dict__:
  97. del self.__dict__[key]
  98. def append(self, key, value=None):
  99. if value==None:
  100. value={}
  101. if isinstance(value, (dict, BaseDocument)):
  102. if not self.__dict__.get(key):
  103. self.__dict__[key] = []
  104. value = self._init_child(value, key)
  105. self.__dict__[key].append(value)
  106. # reference parent document
  107. value.parent_doc = self
  108. return value
  109. else:
  110. raise ValueError, "Document attached to child table must be a dict or BaseDocument, not " + str(type(value))[1:-1]
  111. def extend(self, key, value):
  112. if isinstance(value, list):
  113. for v in value:
  114. self.append(key, v)
  115. else:
  116. raise ValueError
  117. def remove(self, doc):
  118. self.get(doc.parentfield).remove(doc)
  119. def _init_child(self, value, key):
  120. if not self.doctype:
  121. return value
  122. if not isinstance(value, BaseDocument):
  123. if "doctype" not in value:
  124. value["doctype"] = self.get_table_field_doctype(key)
  125. if not value["doctype"]:
  126. raise AttributeError, key
  127. value = get_controller(value["doctype"])(value)
  128. value.init_valid_columns()
  129. value.parent = self.name
  130. value.parenttype = self.doctype
  131. value.parentfield = key
  132. if not getattr(value, "idx", None):
  133. value.idx = len(self.get(key) or []) + 1
  134. if not getattr(value, "name", None):
  135. value.__dict__['__islocal'] = 1
  136. return value
  137. def get_valid_dict(self):
  138. d = {}
  139. for fieldname in self.meta.get_valid_columns():
  140. d[fieldname] = self.get(fieldname)
  141. return d
  142. def init_valid_columns(self):
  143. for key in default_fields:
  144. if key not in self.__dict__:
  145. self.__dict__[key] = None
  146. if self.doctype in ("DocField", "DocPerm") and self.parent in ("DocType", "DocField", "DocPerm"):
  147. from frappe.model.meta import get_table_columns
  148. valid = get_table_columns(self.doctype)
  149. else:
  150. valid = self.meta.get_valid_columns()
  151. for key in valid:
  152. if key not in self.__dict__:
  153. self.__dict__[key] = None
  154. def is_new(self):
  155. return self.get("__islocal")
  156. def as_dict(self, no_nulls=False):
  157. doc = self.get_valid_dict()
  158. doc["doctype"] = self.doctype
  159. for df in self.meta.get_table_fields():
  160. children = self.get(df.fieldname) or []
  161. doc[df.fieldname] = [d.as_dict(no_nulls=no_nulls) for d in children]
  162. if no_nulls:
  163. for k in doc.keys():
  164. if doc[k] is None:
  165. del doc[k]
  166. for key in ("_user_tags", "__islocal", "__onload", "_starred_by"):
  167. if self.get(key):
  168. doc[key] = self.get(key)
  169. return frappe._dict(doc)
  170. def as_json(self):
  171. from frappe.utils.response import json_handler
  172. return json.dumps(self.as_dict(), indent=1, sort_keys=True, default=json_handler)
  173. def get_table_field_doctype(self, fieldname):
  174. return self.meta.get_field(fieldname).options
  175. def get_parentfield_of_doctype(self, doctype):
  176. fieldname = [df.fieldname for df in self.meta.get_table_fields() if df.options==doctype]
  177. return fieldname[0] if fieldname else None
  178. def db_insert(self):
  179. """INSERT the document (with valid columns) in the database."""
  180. if not self.name:
  181. # name will be set by document class in most cases
  182. set_new_name(self)
  183. d = self.get_valid_dict()
  184. columns = d.keys()
  185. try:
  186. frappe.db.sql("""insert into `tab{doctype}`
  187. ({columns}) values ({values})""".format(
  188. doctype = self.doctype,
  189. columns = ", ".join(["`"+c+"`" for c in columns]),
  190. values = ", ".join(["%s"] * len(columns))
  191. ), d.values())
  192. except Exception, e:
  193. if e.args[0]==1062:
  194. if self.meta.autoname=="hash":
  195. self.name = None
  196. self.db_insert()
  197. return
  198. type, value, traceback = sys.exc_info()
  199. frappe.msgprint(_("Duplicate name {0} {1}").format(self.doctype, self.name))
  200. raise frappe.NameError, (self.doctype, self.name, e), traceback
  201. else:
  202. raise
  203. self.set("__islocal", False)
  204. def db_update(self):
  205. if self.get("__islocal") or not self.name:
  206. self.db_insert()
  207. return
  208. d = self.get_valid_dict()
  209. columns = d.keys()
  210. try:
  211. frappe.db.sql("""update `tab{doctype}`
  212. set {values} where name=%s""".format(
  213. doctype = self.doctype,
  214. values = ", ".join(["`"+c+"`=%s" for c in columns])
  215. ), d.values() + [d.get("name")])
  216. except Exception, e:
  217. if e.args[0]==1062:
  218. type, value, traceback = sys.exc_info()
  219. fieldname = str(e).split("'")[-2]
  220. frappe.msgprint(_("{0} must be unique".format(self.meta.get_label(fieldname))))
  221. raise frappe.ValidationError, (self.doctype, self.name, e), traceback
  222. else:
  223. raise
  224. def db_set(self, fieldname, value):
  225. self.set(fieldname, value)
  226. self.set("modified", now())
  227. self.set("modified_by", frappe.session.user)
  228. frappe.db.set_value(self.doctype, self.name, fieldname, value, self.modified, self.modified_by)
  229. def _fix_numeric_types(self):
  230. for df in self.meta.get("fields"):
  231. if df.fieldtype == "Check":
  232. self.set(df.fieldname, cint(self.get(df.fieldname)))
  233. elif self.get(df.fieldname) is not None:
  234. if df.fieldtype == "Int":
  235. self.set(df.fieldname, cint(self.get(df.fieldname)))
  236. elif df.fieldtype in ("Float", "Currency", "Percent"):
  237. self.set(df.fieldname, flt(self.get(df.fieldname)))
  238. if self.docstatus is not None:
  239. self.docstatus = cint(self.docstatus)
  240. def _get_missing_mandatory_fields(self):
  241. """Get mandatory fields that do not have any values"""
  242. def get_msg(df):
  243. if df.fieldtype == "Table":
  244. return "{}: {}: {}".format(_("Error"), _("Data missing in table"), _(df.label))
  245. elif self.parentfield:
  246. return "{}: {} #{}: {}: {}".format(_("Error"), _("Row"), self.idx,
  247. _("Value missing for"), _(df.label))
  248. else:
  249. return "{}: {}: {}".format(_("Error"), _("Value missing for"), _(df.label))
  250. missing = []
  251. for df in self.meta.get("fields", {"reqd": 1}):
  252. if self.get(df.fieldname) in (None, []) or not strip_html(cstr(self.get(df.fieldname))).strip():
  253. missing.append((df.fieldname, get_msg(df)))
  254. return missing
  255. def get_invalid_links(self, is_submittable=False):
  256. def get_msg(df, docname):
  257. if self.parentfield:
  258. return "{} #{}: {}: {}".format(_("Row"), self.idx, _(df.label), docname)
  259. else:
  260. return "{}: {}".format(_(df.label), docname)
  261. invalid_links = []
  262. cancelled_links = []
  263. for df in self.meta.get_link_fields() + self.meta.get("fields",
  264. {"fieldtype":"Dynamic Link"}):
  265. docname = self.get(df.fieldname)
  266. if docname:
  267. if df.fieldtype=="Link":
  268. doctype = df.options
  269. if not doctype:
  270. frappe.throw(_("Options not set for link field {0}").format(df.fieldname))
  271. else:
  272. doctype = self.get(df.options)
  273. if not doctype:
  274. frappe.throw(_("{0} must be set first").format(self.meta.get_label(df.options)))
  275. # MySQL is case insensitive. Preserve case of the original docname in the Link Field.
  276. value = frappe.db.get_value(doctype, docname)
  277. setattr(self, df.fieldname, value)
  278. if not value:
  279. invalid_links.append((df.fieldname, docname, get_msg(df, docname)))
  280. elif (df.fieldname != "amended_from"
  281. and (is_submittable or self.meta.is_submittable) and frappe.get_meta(doctype).is_submittable
  282. and cint(frappe.db.get_value(doctype, docname, "docstatus"))==2):
  283. cancelled_links.append((df.fieldname, docname, get_msg(df, docname)))
  284. return invalid_links, cancelled_links
  285. def _validate_selects(self):
  286. if frappe.flags.in_import:
  287. return
  288. for df in self.meta.get_select_fields():
  289. if df.fieldname=="naming_series" or not (self.get(df.fieldname) and df.options):
  290. continue
  291. options = (df.options or "").split("\n")
  292. # if only empty options
  293. if not filter(None, options):
  294. continue
  295. # strip and set
  296. self.set(df.fieldname, cstr(self.get(df.fieldname)).strip())
  297. value = self.get(df.fieldname)
  298. if value not in options and not (frappe.flags.in_test and value.startswith("_T-")):
  299. # show an elaborate message
  300. prefix = _("Row #{0}:").format(self.idx) if self.get("parentfield") else ""
  301. label = _(self.meta.get_label(df.fieldname))
  302. comma_options = '", "'.join(_(each) for each in options)
  303. frappe.throw(_('{0} {1} cannot be "{2}". It should be one of "{3}"').format(prefix, label,
  304. value, comma_options))
  305. def _validate_constants(self):
  306. if frappe.flags.in_import or self.is_new():
  307. return
  308. constants = [d.fieldname for d in self.meta.get("fields", {"set_only_once": 1})]
  309. if constants:
  310. values = frappe.db.get_value(self.doctype, self.name, constants, as_dict=True)
  311. for fieldname in constants:
  312. if self.get(fieldname) != values.get(fieldname):
  313. frappe.throw(_("Value cannot be changed for {0}").format(self.meta.get_label(fieldname)),
  314. frappe.CannotChangeConstantError)
  315. def _validate_update_after_submit(self):
  316. current = frappe.db.get_value(self.doctype, self.name, "*", as_dict=True)
  317. for key, value in current.iteritems():
  318. df = self.meta.get_field(key)
  319. if df and not df.allow_on_submit and (self.get(key) or value) and self.get(key) != value:
  320. frappe.throw(_("Not allowed to change {0} after submission").format(df.label),
  321. frappe.UpdateAfterSubmitError)
  322. def precision(self, fieldname, parentfield=None):
  323. """Returns float precision for a particular field (or get global default).
  324. :param fieldname: Fieldname for which precision is required.
  325. :param parentfield: If fieldname is in child table."""
  326. from frappe.model.meta import get_field_precision
  327. if parentfield and not isinstance(parentfield, basestring):
  328. parentfield = parentfield.parentfield
  329. cache_key = parentfield or "main"
  330. if not hasattr(self, "_precision"):
  331. self._precision = frappe._dict()
  332. if cache_key not in self._precision:
  333. self._precision[cache_key] = frappe._dict()
  334. if fieldname not in self._precision[cache_key]:
  335. self._precision[cache_key][fieldname] = None
  336. doctype = self.meta.get_field(parentfield).options if parentfield else self.doctype
  337. df = frappe.get_meta(doctype).get_field(fieldname)
  338. if df.fieldtype in ("Currency", "Float", "Percent"):
  339. self._precision[cache_key][fieldname] = get_field_precision(df, self)
  340. return self._precision[cache_key][fieldname]
  341. def get_formatted(self, fieldname, doc=None, currency=None):
  342. from frappe.utils.formatters import format_value
  343. df = self.meta.get_field(fieldname)
  344. if not df and fieldname in default_fields:
  345. from frappe.model.meta import get_default_df
  346. df = get_default_df(fieldname)
  347. return format_value(self.get(fieldname), df=df, doc=doc or self, currency=currency)
  348. def is_print_hide(self, fieldname, for_print=True):
  349. """Returns true if fieldname is to be hidden for print.
  350. Print Hide can be set via the Print Format Builder or in the controller as a list
  351. of hidden fields. Example
  352. class MyDoc(Document):
  353. def __setup__(self):
  354. self.print_hide = ["field1", "field2"]
  355. :param fieldname: Fieldname to be checked if hidden.
  356. """
  357. df = self.meta.get_field(fieldname)
  358. return df and (df.get("__print_hide") or df.print_hide)
  359. def in_format_data(self, fieldname):
  360. """Returns True if shown via Print Format::`format_data` property.
  361. Called from within standard print format."""
  362. doc = getattr(self, "parent_doc", self)
  363. if hasattr(doc, "format_data_map"):
  364. return fieldname in doc.format_data_map
  365. else:
  366. return True
  367. def _filter(data, filters, limit=None):
  368. """pass filters as:
  369. {"key": "val", "key": ["!=", "val"],
  370. "key": ["in", "val"], "key": ["not in", "val"], "key": "^val",
  371. "key" : True (exists), "key": False (does not exist) }"""
  372. out = []
  373. for d in data:
  374. add = True
  375. for f in filters:
  376. fval = filters[f]
  377. if fval is True:
  378. fval = ("not None", fval)
  379. elif fval is False:
  380. fval = ("None", fval)
  381. elif not isinstance(fval, (tuple, list)):
  382. if isinstance(fval, basestring) and fval.startswith("^"):
  383. fval = ("^", fval[1:])
  384. else:
  385. fval = ("=", fval)
  386. if not frappe.compare(getattr(d, f, None), fval[0], fval[1]):
  387. add = False
  388. break
  389. if add:
  390. out.append(d)
  391. if limit and (len(out)-1)==limit:
  392. break
  393. return out