25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.

base_document.py 18 KiB

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