You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1099 lines
33 KiB

  1. # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
  2. # License: MIT. See LICENSE
  3. import datetime
  4. import frappe
  5. from frappe import _
  6. from frappe.model import child_table_fields, default_fields, display_fieldtypes, table_fields
  7. from frappe.model.naming import set_new_name
  8. from frappe.model.utils.link_count import notify_link_count
  9. from frappe.modules import load_doctype_module
  10. from frappe.utils import cast_fieldtype, cint, cstr, flt, now, sanitize_html, strip_html
  11. from frappe.utils.html_utils import unescape_html
  12. from frappe.model.docstatus import DocStatus
  13. max_positive_value = {
  14. 'smallint': 2 ** 15,
  15. 'int': 2 ** 31,
  16. 'bigint': 2 ** 63
  17. }
  18. DOCTYPES_FOR_DOCTYPE = ('DocType', 'DocField', 'DocPerm', 'DocType Action', 'DocType Link')
  19. def get_controller(doctype):
  20. """Returns the **class** object of the given DocType.
  21. For `custom` type, returns `frappe.model.document.Document`.
  22. :param doctype: DocType name as string."""
  23. def _get_controller():
  24. from frappe.model.document import Document
  25. from frappe.utils.nestedset import NestedSet
  26. module_name, custom = frappe.db.get_value(
  27. "DocType", doctype, ("module", "custom"), cache=True
  28. ) or ("Core", False)
  29. if custom:
  30. is_tree = frappe.db.get_value(
  31. "DocType", doctype, "is_tree", ignore=True, cache=True
  32. )
  33. _class = NestedSet if is_tree else Document
  34. else:
  35. class_overrides = frappe.get_hooks('override_doctype_class')
  36. if class_overrides and class_overrides.get(doctype):
  37. import_path = class_overrides[doctype][-1]
  38. module_path, classname = import_path.rsplit('.', 1)
  39. module = frappe.get_module(module_path)
  40. if not hasattr(module, classname):
  41. raise ImportError('{0}: {1} does not exist in module {2}'.format(doctype, classname, module_path))
  42. else:
  43. module = load_doctype_module(doctype, module_name)
  44. classname = doctype.replace(" ", "").replace("-", "")
  45. if hasattr(module, classname):
  46. _class = getattr(module, classname)
  47. if issubclass(_class, BaseDocument):
  48. _class = getattr(module, classname)
  49. else:
  50. raise ImportError(doctype)
  51. else:
  52. raise ImportError(doctype)
  53. return _class
  54. if frappe.local.dev_server:
  55. return _get_controller()
  56. site_controllers = frappe.controllers.setdefault(frappe.local.site, {})
  57. if doctype not in site_controllers:
  58. site_controllers[doctype] = _get_controller()
  59. return site_controllers[doctype]
  60. class BaseDocument(object):
  61. ignore_in_setter = ("doctype", "_meta", "meta", "_table_fields", "_valid_columns")
  62. def __init__(self, d):
  63. if d.get("doctype"):
  64. self.doctype = d["doctype"]
  65. self.update(d)
  66. self.dont_update_if_missing = []
  67. if hasattr(self, "__setup__"):
  68. self.__setup__()
  69. @property
  70. def meta(self):
  71. if not getattr(self, "_meta", None):
  72. self._meta = frappe.get_meta(self.doctype)
  73. return self._meta
  74. def __getstate__(self):
  75. self._meta = None
  76. return self.__dict__
  77. def update(self, d):
  78. """ Update multiple fields of a doctype using a dictionary of key-value pairs.
  79. Example:
  80. doc.update({
  81. "user": "admin",
  82. "balance": 42000
  83. })
  84. """
  85. # set name first, as it is used a reference in child document
  86. if "name" in d:
  87. self.name = d["name"]
  88. for key, value in d.items():
  89. self.set(key, value)
  90. return self
  91. def update_if_missing(self, d):
  92. """Set default values for fields without existing values"""
  93. if isinstance(d, BaseDocument):
  94. d = d.get_valid_dict()
  95. for key, value in d.items():
  96. if (
  97. value is not None
  98. and self.get(key) is None
  99. # dont_update_if_missing is a list of fieldnames
  100. # for which you don't want to set default value
  101. and key not in self.dont_update_if_missing
  102. ):
  103. self.set(key, value)
  104. def get_db_value(self, key):
  105. return frappe.db.get_value(self.doctype, self.name, key)
  106. def get(self, key=None, filters=None, limit=None, default=None):
  107. if key:
  108. if isinstance(key, dict):
  109. return _filter(self.get_all_children(), key, limit=limit)
  110. if filters:
  111. if isinstance(filters, dict):
  112. value = _filter(self.__dict__.get(key, []), filters, limit=limit)
  113. else:
  114. default = filters
  115. filters = None
  116. value = self.__dict__.get(key, default)
  117. else:
  118. value = self.__dict__.get(key, default)
  119. if value is None and key in (
  120. d.fieldname for d in self.meta.get_table_fields()
  121. ):
  122. value = []
  123. self.set(key, value)
  124. if limit and isinstance(value, (list, tuple)) and len(value) > limit:
  125. value = value[:limit]
  126. return value
  127. else:
  128. return self.__dict__
  129. def getone(self, key, filters=None):
  130. return self.get(key, filters=filters, limit=1)[0]
  131. def set(self, key, value, as_value=False):
  132. if key in self.ignore_in_setter:
  133. return
  134. if isinstance(value, list) and not as_value:
  135. self.__dict__[key] = []
  136. self.extend(key, value)
  137. else:
  138. self.__dict__[key] = value
  139. def delete_key(self, key):
  140. if key in self.__dict__:
  141. del self.__dict__[key]
  142. def append(self, key, value=None):
  143. """ Append an item to a child table.
  144. Example:
  145. doc.append("childtable", {
  146. "child_table_field": "value",
  147. "child_table_int_field": 0,
  148. ...
  149. })
  150. """
  151. if value is None:
  152. value={}
  153. if isinstance(value, (dict, BaseDocument)):
  154. if not self.__dict__.get(key):
  155. self.__dict__[key] = []
  156. value = self._init_child(value, key)
  157. self.__dict__[key].append(value)
  158. # reference parent document
  159. value.parent_doc = self
  160. return value
  161. else:
  162. # metaclasses may have arbitrary lists
  163. # which we can ignore
  164. if (getattr(self, '_metaclass', None)
  165. or self.__class__.__name__ in ('Meta', 'FormMeta', 'DocField')):
  166. return value
  167. raise ValueError(
  168. 'Document for field "{0}" attached to child table of "{1}" must be a dict or BaseDocument, not {2} ({3})'.format(key,
  169. self.name, str(type(value))[1:-1], value)
  170. )
  171. def extend(self, key, value):
  172. if isinstance(value, list):
  173. for v in value:
  174. self.append(key, v)
  175. else:
  176. raise ValueError
  177. def remove(self, doc):
  178. # Usage: from the parent doc, pass the child table doc
  179. # to remove that child doc from the child table, thus removing it from the parent doc
  180. if doc.get("parentfield"):
  181. self.get(doc.parentfield).remove(doc)
  182. def _init_child(self, value, key):
  183. if not self.doctype:
  184. return value
  185. if not isinstance(value, BaseDocument):
  186. value["doctype"] = self.get_table_field_doctype(key)
  187. if not value["doctype"]:
  188. raise AttributeError(key)
  189. value = get_controller(value["doctype"])(value)
  190. value.init_valid_columns()
  191. value.parent = self.name
  192. value.parenttype = self.doctype
  193. value.parentfield = key
  194. if value.docstatus is None:
  195. value.docstatus = DocStatus.draft()
  196. if not getattr(value, "idx", None):
  197. value.idx = len(self.get(key) or []) + 1
  198. if not getattr(value, "name", None):
  199. value.__dict__['__islocal'] = 1
  200. return value
  201. def get_valid_dict(self, sanitize=True, convert_dates_to_str=False, ignore_nulls=False, ignore_virtual=False):
  202. d = frappe._dict()
  203. for fieldname in self.meta.get_valid_columns():
  204. d[fieldname] = self.get(fieldname)
  205. # if no need for sanitization and value is None, continue
  206. if not sanitize and d[fieldname] is None:
  207. continue
  208. df = self.meta.get_field(fieldname)
  209. if df and df.get("is_virtual"):
  210. if ignore_virtual:
  211. del d[fieldname]
  212. continue
  213. from frappe.utils.safe_exec import get_safe_globals
  214. if d[fieldname] is None:
  215. if df.get("options"):
  216. d[fieldname] = frappe.safe_eval(
  217. code=df.get("options"),
  218. eval_globals=get_safe_globals(),
  219. eval_locals={"doc": self},
  220. )
  221. else:
  222. _val = getattr(self, fieldname, None)
  223. if _val and not callable(_val):
  224. d[fieldname] = _val
  225. elif df:
  226. if df.fieldtype=="Check":
  227. d[fieldname] = 1 if cint(d[fieldname]) else 0
  228. elif df.fieldtype=="Int" and not isinstance(d[fieldname], int):
  229. d[fieldname] = cint(d[fieldname])
  230. elif df.fieldtype in ("Currency", "Float", "Percent") and not isinstance(d[fieldname], float):
  231. d[fieldname] = flt(d[fieldname])
  232. elif df.fieldtype in ("Datetime", "Date", "Time") and d[fieldname]=="":
  233. d[fieldname] = None
  234. elif df.get("unique") and cstr(d[fieldname]).strip()=="":
  235. # unique empty field should be set to None
  236. d[fieldname] = None
  237. if isinstance(d[fieldname], list) and df.fieldtype not in table_fields:
  238. frappe.throw(_('Value for {0} cannot be a list').format(_(df.label)))
  239. if convert_dates_to_str and isinstance(d[fieldname], (
  240. datetime.datetime,
  241. datetime.date,
  242. datetime.time,
  243. datetime.timedelta
  244. )):
  245. d[fieldname] = str(d[fieldname])
  246. if d[fieldname] is None and ignore_nulls:
  247. del d[fieldname]
  248. return d
  249. def init_valid_columns(self):
  250. for key in default_fields:
  251. if key not in self.__dict__:
  252. self.__dict__[key] = None
  253. if self.__dict__[key] is None:
  254. if key == "docstatus":
  255. self.docstatus = DocStatus.draft()
  256. elif key == "idx":
  257. self.__dict__[key] = 0
  258. for key in self.get_valid_columns():
  259. if key not in self.__dict__:
  260. self.__dict__[key] = None
  261. def get_valid_columns(self):
  262. if self.doctype not in frappe.local.valid_columns:
  263. if self.doctype in DOCTYPES_FOR_DOCTYPE:
  264. from frappe.model.meta import get_table_columns
  265. valid = get_table_columns(self.doctype)
  266. else:
  267. valid = self.meta.get_valid_columns()
  268. frappe.local.valid_columns[self.doctype] = valid
  269. return frappe.local.valid_columns[self.doctype]
  270. def is_new(self):
  271. return self.get("__islocal")
  272. @property
  273. def docstatus(self):
  274. return DocStatus(self.get("docstatus"))
  275. @docstatus.setter
  276. def docstatus(self, value):
  277. self.__dict__["docstatus"] = DocStatus(cint(value))
  278. def as_dict(self, no_nulls=False, no_default_fields=False, convert_dates_to_str=False, no_child_table_fields=False):
  279. doc = self.get_valid_dict(convert_dates_to_str=convert_dates_to_str)
  280. doc["doctype"] = self.doctype
  281. for df in self.meta.get_table_fields():
  282. children = self.get(df.fieldname) or []
  283. doc[df.fieldname] = [
  284. d.as_dict(
  285. convert_dates_to_str=convert_dates_to_str,
  286. no_nulls=no_nulls,
  287. no_default_fields=no_default_fields,
  288. no_child_table_fields=no_child_table_fields
  289. ) for d in children
  290. ]
  291. if no_nulls:
  292. for k in list(doc):
  293. if doc[k] is None:
  294. del doc[k]
  295. if no_default_fields:
  296. for k in list(doc):
  297. if k in default_fields:
  298. del doc[k]
  299. if no_child_table_fields:
  300. for k in list(doc):
  301. if k in child_table_fields:
  302. del doc[k]
  303. for key in ("_user_tags", "__islocal", "__onload", "_liked_by", "__run_link_triggers", "__unsaved"):
  304. if self.get(key):
  305. doc[key] = self.get(key)
  306. return doc
  307. def as_json(self):
  308. return frappe.as_json(self.as_dict())
  309. def get_table_field_doctype(self, fieldname):
  310. try:
  311. return self.meta.get_field(fieldname).options
  312. except AttributeError:
  313. if self.doctype == 'DocType':
  314. return dict(links='DocType Link', actions='DocType Action', states='DocType State').get(fieldname)
  315. raise
  316. def get_parentfield_of_doctype(self, doctype):
  317. fieldname = [df.fieldname for df in self.meta.get_table_fields() if df.options==doctype]
  318. return fieldname[0] if fieldname else None
  319. def db_insert(self, ignore_if_duplicate=False):
  320. """INSERT the document (with valid columns) in the database.
  321. args:
  322. ignore_if_duplicate: ignore primary key collision
  323. at database level (postgres)
  324. in python (mariadb)
  325. """
  326. if not self.name:
  327. # name will be set by document class in most cases
  328. set_new_name(self)
  329. conflict_handler = ""
  330. # On postgres we can't implcitly ignore PK collision
  331. # So instruct pg to ignore `name` field conflicts
  332. if ignore_if_duplicate and frappe.db.db_type == "postgres":
  333. conflict_handler = "on conflict (name) do nothing"
  334. if not self.creation:
  335. self.creation = self.modified = now()
  336. self.created_by = self.modified_by = frappe.session.user
  337. # if doctype is "DocType", don't insert null values as we don't know who is valid yet
  338. d = self.get_valid_dict(
  339. convert_dates_to_str=True,
  340. ignore_nulls=self.doctype in DOCTYPES_FOR_DOCTYPE,
  341. ignore_virtual=True,
  342. )
  343. columns = list(d)
  344. try:
  345. frappe.db.sql("""INSERT INTO `tab{doctype}` ({columns})
  346. VALUES ({values}) {conflict_handler}""".format(
  347. doctype=self.doctype,
  348. columns=", ".join("`"+c+"`" for c in columns),
  349. values=", ".join(["%s"] * len(columns)),
  350. conflict_handler=conflict_handler
  351. ), list(d.values()))
  352. except Exception as e:
  353. if frappe.db.is_primary_key_violation(e):
  354. if self.meta.autoname=="hash":
  355. # hash collision? try again
  356. frappe.flags.retry_count = (frappe.flags.retry_count or 0) + 1
  357. if frappe.flags.retry_count > 5 and not frappe.flags.in_test:
  358. raise
  359. self.name = None
  360. self.db_insert()
  361. return
  362. if not ignore_if_duplicate:
  363. frappe.msgprint(_("{0} {1} already exists")
  364. .format(self.doctype, frappe.bold(self.name)),
  365. title=_("Duplicate Name"), indicator="red")
  366. raise frappe.DuplicateEntryError(self.doctype, self.name, e)
  367. elif frappe.db.is_unique_key_violation(e):
  368. # unique constraint
  369. self.show_unique_validation_message(e)
  370. else:
  371. raise
  372. self.set("__islocal", False)
  373. def db_update(self):
  374. if self.get("__islocal") or not self.name:
  375. self.db_insert()
  376. return
  377. d = self.get_valid_dict(convert_dates_to_str=True, ignore_nulls = self.doctype in DOCTYPES_FOR_DOCTYPE)
  378. # don't update name, as case might've been changed
  379. name = cstr(d['name'])
  380. del d['name']
  381. columns = list(d)
  382. try:
  383. frappe.db.sql("""UPDATE `tab{doctype}`
  384. SET {values} WHERE `name`=%s""".format(
  385. doctype = self.doctype,
  386. values = ", ".join("`"+c+"`=%s" for c in columns)
  387. ), list(d.values()) + [name])
  388. except Exception as e:
  389. if frappe.db.is_unique_key_violation(e):
  390. self.show_unique_validation_message(e)
  391. else:
  392. raise
  393. def db_update_all(self):
  394. """Raw update parent + children
  395. DOES NOT VALIDATE AND CALL TRIGGERS"""
  396. self.db_update()
  397. for df in self.meta.get_table_fields():
  398. for doc in self.get(df.fieldname):
  399. doc.db_update()
  400. def show_unique_validation_message(self, e):
  401. if frappe.db.db_type != 'postgres':
  402. fieldname = str(e).split("'")[-2]
  403. label = None
  404. # MariaDB gives key_name in error. Extracting fieldname from key name
  405. try:
  406. fieldname = self.get_field_name_by_key_name(fieldname)
  407. except IndexError:
  408. pass
  409. label = self.get_label_from_fieldname(fieldname)
  410. frappe.msgprint(_("{0} must be unique").format(label or fieldname))
  411. # this is used to preserve traceback
  412. raise frappe.UniqueValidationError(self.doctype, self.name, e)
  413. def get_field_name_by_key_name(self, key_name):
  414. """MariaDB stores a mapping between `key_name` and `column_name`.
  415. This function returns the `column_name` associated with the `key_name` passed
  416. Args:
  417. key_name (str): The name of the database index.
  418. Raises:
  419. IndexError: If the key is not found in the table.
  420. Returns:
  421. str: The column name associated with the key.
  422. """
  423. return frappe.db.sql(f"""
  424. SHOW
  425. INDEX
  426. FROM
  427. `tab{self.doctype}`
  428. WHERE
  429. key_name=%s
  430. AND
  431. Non_unique=0
  432. """, key_name, as_dict=True)[0].get("Column_name")
  433. def get_label_from_fieldname(self, fieldname):
  434. """Returns the associated label for fieldname
  435. Args:
  436. fieldname (str): The fieldname in the DocType to use to pull the label.
  437. Returns:
  438. str: The label associated with the fieldname, if found, otherwise `None`.
  439. """
  440. df = self.meta.get_field(fieldname)
  441. if df:
  442. return df.label
  443. def update_modified(self):
  444. """Update modified timestamp"""
  445. self.set("modified", now())
  446. frappe.db.set_value(self.doctype, self.name, 'modified', self.modified, update_modified=False)
  447. def _fix_numeric_types(self):
  448. for df in self.meta.get("fields"):
  449. if df.fieldtype == "Check":
  450. self.set(df.fieldname, cint(self.get(df.fieldname)))
  451. elif self.get(df.fieldname) is not None:
  452. if df.fieldtype == "Int":
  453. self.set(df.fieldname, cint(self.get(df.fieldname)))
  454. elif df.fieldtype in ("Float", "Currency", "Percent"):
  455. self.set(df.fieldname, flt(self.get(df.fieldname)))
  456. if self.docstatus is not None:
  457. self.docstatus = DocStatus(cint(self.docstatus))
  458. def _get_missing_mandatory_fields(self):
  459. """Get mandatory fields that do not have any values"""
  460. def get_msg(df):
  461. if df.fieldtype in table_fields:
  462. return "{}: {}: {}".format(_("Error"), _("Data missing in table"), _(df.label))
  463. # check if parentfield exists (only applicable for child table doctype)
  464. elif self.get("parentfield"):
  465. return "{}: {} {} #{}: {}: {}".format(_("Error"), frappe.bold(_(self.doctype)),
  466. _("Row"), self.idx, _("Value missing for"), _(df.label))
  467. return _("Error: Value missing for {0}: {1}").format(_(df.parent), _(df.label))
  468. missing = []
  469. for df in self.meta.get("fields", {"reqd": ('=', 1)}):
  470. if self.get(df.fieldname) in (None, []) or not strip_html(cstr(self.get(df.fieldname))).strip():
  471. missing.append((df.fieldname, get_msg(df)))
  472. # check for missing parent and parenttype
  473. if self.meta.istable:
  474. for fieldname in ("parent", "parenttype"):
  475. if not self.get(fieldname):
  476. missing.append((fieldname, get_msg(frappe._dict(label=fieldname))))
  477. return missing
  478. def get_invalid_links(self, is_submittable=False):
  479. """Returns list of invalid links and also updates fetch values if not set"""
  480. def get_msg(df, docname):
  481. # check if parentfield exists (only applicable for child table doctype)
  482. if self.get("parentfield"):
  483. return "{} #{}: {}: {}".format(_("Row"), self.idx, _(df.label), docname)
  484. return "{}: {}".format(_(df.label), docname)
  485. invalid_links = []
  486. cancelled_links = []
  487. for df in (self.meta.get_link_fields()
  488. + self.meta.get("fields", {"fieldtype": ('=', "Dynamic Link")})):
  489. docname = self.get(df.fieldname)
  490. if docname:
  491. if df.fieldtype=="Link":
  492. doctype = df.options
  493. if not doctype:
  494. frappe.throw(_("Options not set for link field {0}").format(df.fieldname))
  495. else:
  496. doctype = self.get(df.options)
  497. if not doctype:
  498. frappe.throw(_("{0} must be set first").format(self.meta.get_label(df.options)))
  499. # MySQL is case insensitive. Preserve case of the original docname in the Link Field.
  500. # get a map of values ot fetch along with this link query
  501. # that are mapped as link_fieldname.source_fieldname in Options of
  502. # Readonly or Data or Text type fields
  503. fields_to_fetch = [
  504. _df for _df in self.meta.get_fields_to_fetch(df.fieldname)
  505. if
  506. not _df.get('fetch_if_empty')
  507. or (_df.get('fetch_if_empty') and not self.get(_df.fieldname))
  508. ]
  509. if not frappe.get_meta(doctype).get('is_virtual'):
  510. if not fields_to_fetch:
  511. # cache a single value type
  512. values = frappe._dict(name=frappe.db.get_value(doctype, docname,
  513. 'name', cache=True))
  514. else:
  515. values_to_fetch = ['name'] + [_df.fetch_from.split('.')[-1]
  516. for _df in fields_to_fetch]
  517. # don't cache if fetching other values too
  518. values = frappe.db.get_value(doctype, docname,
  519. values_to_fetch, as_dict=True)
  520. if frappe.get_meta(doctype).issingle:
  521. values.name = doctype
  522. if frappe.get_meta(doctype).get('is_virtual'):
  523. values = frappe.get_doc(doctype, docname)
  524. if values:
  525. setattr(self, df.fieldname, values.name)
  526. for _df in fields_to_fetch:
  527. if self.is_new() or not self.docstatus.is_submitted() or _df.allow_on_submit:
  528. self.set_fetch_from_value(doctype, _df, values)
  529. notify_link_count(doctype, docname)
  530. if not values.name:
  531. invalid_links.append((df.fieldname, docname, get_msg(df, docname)))
  532. elif (df.fieldname != "amended_from"
  533. and (is_submittable or self.meta.is_submittable) and frappe.get_meta(doctype).is_submittable
  534. and cint(frappe.db.get_value(doctype, docname, "docstatus")) == DocStatus.cancelled()):
  535. cancelled_links.append((df.fieldname, docname, get_msg(df, docname)))
  536. return invalid_links, cancelled_links
  537. def set_fetch_from_value(self, doctype, df, values):
  538. fetch_from_fieldname = df.fetch_from.split('.')[-1]
  539. value = values[fetch_from_fieldname]
  540. if df.fieldtype in ['Small Text', 'Text', 'Data']:
  541. from frappe.model.meta import get_default_df
  542. fetch_from_df = get_default_df(fetch_from_fieldname) or frappe.get_meta(doctype).get_field(fetch_from_fieldname)
  543. if not fetch_from_df:
  544. frappe.throw(
  545. _('Please check the value of "Fetch From" set for field {0}').format(frappe.bold(df.label)),
  546. title = _('Wrong Fetch From value')
  547. )
  548. fetch_from_ft = fetch_from_df.get('fieldtype')
  549. if fetch_from_ft == 'Text Editor' and value:
  550. value = unescape_html(strip_html(value))
  551. setattr(self, df.fieldname, value)
  552. def _validate_selects(self):
  553. if frappe.flags.in_import:
  554. return
  555. for df in self.meta.get_select_fields():
  556. if df.fieldname=="naming_series" or not (self.get(df.fieldname) and df.options):
  557. continue
  558. options = (df.options or "").split("\n")
  559. # if only empty options
  560. if not filter(None, options):
  561. continue
  562. # strip and set
  563. self.set(df.fieldname, cstr(self.get(df.fieldname)).strip())
  564. value = self.get(df.fieldname)
  565. if value not in options and not (frappe.flags.in_test and value.startswith("_T-")):
  566. # show an elaborate message
  567. prefix = _("Row #{0}:").format(self.idx) if self.get("parentfield") else ""
  568. label = _(self.meta.get_label(df.fieldname))
  569. comma_options = '", "'.join(_(each) for each in options)
  570. frappe.throw(_('{0} {1} cannot be "{2}". It should be one of "{3}"').format(prefix, label,
  571. value, comma_options))
  572. def _validate_data_fields(self):
  573. # data_field options defined in frappe.model.data_field_options
  574. for data_field in self.meta.get_data_fields():
  575. data = self.get(data_field.fieldname)
  576. data_field_options = data_field.get("options")
  577. old_fieldtype = data_field.get("oldfieldtype")
  578. if old_fieldtype and old_fieldtype != "Data":
  579. continue
  580. if data_field_options == "Email":
  581. if (self.owner in frappe.STANDARD_USERS) and (data in frappe.STANDARD_USERS):
  582. continue
  583. for email_address in frappe.utils.split_emails(data):
  584. frappe.utils.validate_email_address(email_address, throw=True)
  585. if data_field_options == "Name":
  586. frappe.utils.validate_name(data, throw=True)
  587. if data_field_options == "Phone":
  588. frappe.utils.validate_phone_number(data, throw=True)
  589. if data_field_options == "URL":
  590. if not data:
  591. continue
  592. frappe.utils.validate_url(data, throw=True)
  593. def _validate_constants(self):
  594. if frappe.flags.in_import or self.is_new() or self.flags.ignore_validate_constants:
  595. return
  596. constants = [d.fieldname for d in self.meta.get("fields", {"set_only_once": ('=',1)})]
  597. if constants:
  598. values = frappe.db.get_value(self.doctype, self.name, constants, as_dict=True)
  599. for fieldname in constants:
  600. df = self.meta.get_field(fieldname)
  601. # This conversion to string only when fieldtype is Date
  602. if df.fieldtype == 'Date' or df.fieldtype == 'Datetime':
  603. value = str(values.get(fieldname))
  604. else:
  605. value = values.get(fieldname)
  606. if self.get(fieldname) != value:
  607. frappe.throw(_("Value cannot be changed for {0}").format(self.meta.get_label(fieldname)),
  608. frappe.CannotChangeConstantError)
  609. def _validate_length(self):
  610. if frappe.flags.in_install:
  611. return
  612. if self.meta.issingle:
  613. # single doctype value type is mediumtext
  614. return
  615. type_map = frappe.db.type_map
  616. for fieldname, value in self.get_valid_dict(ignore_virtual=True).items():
  617. df = self.meta.get_field(fieldname)
  618. if not df or df.fieldtype == 'Check':
  619. # skip standard fields and Check fields
  620. continue
  621. column_type = type_map[df.fieldtype][0] or None
  622. if column_type == 'varchar':
  623. default_column_max_length = type_map[df.fieldtype][1] or None
  624. max_length = cint(df.get("length")) or cint(default_column_max_length)
  625. if len(cstr(value)) > max_length:
  626. self.throw_length_exceeded_error(df, max_length, value)
  627. elif column_type in ('int', 'bigint', 'smallint'):
  628. max_length = max_positive_value[column_type]
  629. if abs(cint(value)) > max_length:
  630. self.throw_length_exceeded_error(df, max_length, value)
  631. def _validate_code_fields(self):
  632. for field in self.meta.get_code_fields():
  633. code_string = self.get(field.fieldname)
  634. language = field.get("options")
  635. if language == "Python":
  636. frappe.utils.validate_python_code(code_string, fieldname=field.label, is_expression=False)
  637. elif language == "PythonExpression":
  638. frappe.utils.validate_python_code(code_string, fieldname=field.label)
  639. def throw_length_exceeded_error(self, df, max_length, value):
  640. # check if parentfield exists (only applicable for child table doctype)
  641. if self.get("parentfield"):
  642. reference = _("{0}, Row {1}").format(_(self.doctype), self.idx)
  643. else:
  644. reference = "{0} {1}".format(_(self.doctype), self.name)
  645. frappe.throw(_("{0}: '{1}' ({3}) will get truncated, as max characters allowed is {2}")\
  646. .format(reference, _(df.label), max_length, value), frappe.CharacterLengthExceededError, title=_('Value too big'))
  647. def _validate_update_after_submit(self):
  648. # get the full doc with children
  649. db_values = frappe.get_doc(self.doctype, self.name).as_dict()
  650. for key in self.as_dict():
  651. df = self.meta.get_field(key)
  652. db_value = db_values.get(key)
  653. if df and not df.allow_on_submit and (self.get(key) or db_value):
  654. if df.fieldtype in table_fields:
  655. # just check if the table size has changed
  656. # individual fields will be checked in the loop for children
  657. self_value = len(self.get(key))
  658. db_value = len(db_value)
  659. else:
  660. self_value = self.get_value(key)
  661. # Postgres stores values as `datetime.time`, MariaDB as `timedelta`
  662. if isinstance(self_value, datetime.timedelta) and isinstance(db_value, datetime.time):
  663. db_value = datetime.timedelta(hours=db_value.hour, minutes=db_value.minute, seconds=db_value.second, microseconds=db_value.microsecond)
  664. if self_value != db_value:
  665. frappe.throw(_("Not allowed to change {0} after submission").format(df.label),
  666. frappe.UpdateAfterSubmitError)
  667. def _sanitize_content(self):
  668. """Sanitize HTML and Email in field values. Used to prevent XSS.
  669. - Ignore if 'Ignore XSS Filter' is checked or fieldtype is 'Code'
  670. """
  671. from bs4 import BeautifulSoup
  672. if frappe.flags.in_install:
  673. return
  674. for fieldname, value in self.get_valid_dict(ignore_virtual=True).items():
  675. if not value or not isinstance(value, str):
  676. continue
  677. value = frappe.as_unicode(value)
  678. if (u"<" not in value and u">" not in value):
  679. # doesn't look like html so no need
  680. continue
  681. elif "<!-- markdown -->" in value and not bool(BeautifulSoup(value, "html.parser").find()):
  682. # should be handled separately via the markdown converter function
  683. continue
  684. df = self.meta.get_field(fieldname)
  685. sanitized_value = value
  686. if df and (df.get("ignore_xss_filter")
  687. or (df.get("fieldtype") in ("Data", "Small Text", "Text") and df.get("options")=="Email")
  688. or df.get("fieldtype") in ("Attach", "Attach Image", "Barcode", "Code")
  689. # cancelled and submit but not update after submit should be ignored
  690. or self.docstatus.is_cancelled()
  691. or (self.docstatus.is_submitted() and not df.get("allow_on_submit"))):
  692. continue
  693. else:
  694. sanitized_value = sanitize_html(value, linkify=df and df.fieldtype=='Text Editor')
  695. self.set(fieldname, sanitized_value)
  696. def _save_passwords(self):
  697. """Save password field values in __Auth table"""
  698. from frappe.utils.password import set_encrypted_password, remove_encrypted_password
  699. if self.flags.ignore_save_passwords is True:
  700. return
  701. for df in self.meta.get('fields', {'fieldtype': ('=', 'Password')}):
  702. if self.flags.ignore_save_passwords and df.fieldname in self.flags.ignore_save_passwords: continue
  703. new_password = self.get(df.fieldname)
  704. if not new_password:
  705. remove_encrypted_password(self.doctype, self.name, df.fieldname)
  706. if new_password and not self.is_dummy_password(new_password):
  707. # is not a dummy password like '*****'
  708. set_encrypted_password(self.doctype, self.name, new_password, df.fieldname)
  709. # set dummy password like '*****'
  710. self.set(df.fieldname, '*'*len(new_password))
  711. def get_password(self, fieldname='password', raise_exception=True):
  712. from frappe.utils.password import get_decrypted_password
  713. if self.get(fieldname) and not self.is_dummy_password(self.get(fieldname)):
  714. return self.get(fieldname)
  715. return get_decrypted_password(self.doctype, self.name, fieldname, raise_exception=raise_exception)
  716. def is_dummy_password(self, pwd):
  717. return ''.join(set(pwd))=='*'
  718. def precision(self, fieldname, parentfield=None):
  719. """Returns float precision for a particular field (or get global default).
  720. :param fieldname: Fieldname for which precision is required.
  721. :param parentfield: If fieldname is in child table."""
  722. from frappe.model.meta import get_field_precision
  723. if parentfield and not isinstance(parentfield, str) and parentfield.get("parentfield"):
  724. parentfield = parentfield.parentfield
  725. cache_key = parentfield or "main"
  726. if not hasattr(self, "_precision"):
  727. self._precision = frappe._dict()
  728. if cache_key not in self._precision:
  729. self._precision[cache_key] = frappe._dict()
  730. if fieldname not in self._precision[cache_key]:
  731. self._precision[cache_key][fieldname] = None
  732. doctype = self.meta.get_field(parentfield).options if parentfield else self.doctype
  733. df = frappe.get_meta(doctype).get_field(fieldname)
  734. if df.fieldtype in ("Currency", "Float", "Percent"):
  735. self._precision[cache_key][fieldname] = get_field_precision(df, self)
  736. return self._precision[cache_key][fieldname]
  737. def get_formatted(self, fieldname, doc=None, currency=None, absolute_value=False, translated=False, format=None):
  738. from frappe.utils.formatters import format_value
  739. df = self.meta.get_field(fieldname)
  740. if not df:
  741. from frappe.model.meta import get_default_df
  742. df = get_default_df(fieldname)
  743. if not currency and df:
  744. currency = self.get(df.get("options"))
  745. if not frappe.db.exists('Currency', currency, cache=True):
  746. currency = None
  747. val = self.get(fieldname)
  748. if translated:
  749. val = _(val)
  750. if not doc:
  751. doc = getattr(self, "parent_doc", None) or self
  752. if (absolute_value or doc.get('absolute_value')) and isinstance(val, (int, float)):
  753. val = abs(self.get(fieldname))
  754. return format_value(val, df=df, doc=doc, currency=currency, format=format)
  755. def is_print_hide(self, fieldname, df=None, for_print=True):
  756. """Returns true if fieldname is to be hidden for print.
  757. Print Hide can be set via the Print Format Builder or in the controller as a list
  758. of hidden fields. Example
  759. class MyDoc(Document):
  760. def __setup__(self):
  761. self.print_hide = ["field1", "field2"]
  762. :param fieldname: Fieldname to be checked if hidden.
  763. """
  764. meta_df = self.meta.get_field(fieldname)
  765. if meta_df and meta_df.get("__print_hide"):
  766. return True
  767. print_hide = 0
  768. if self.get(fieldname)==0 and not self.meta.istable:
  769. print_hide = ( df and df.print_hide_if_no_value ) or ( meta_df and meta_df.print_hide_if_no_value )
  770. if not print_hide:
  771. if df and df.print_hide is not None:
  772. print_hide = df.print_hide
  773. elif meta_df:
  774. print_hide = meta_df.print_hide
  775. return print_hide
  776. def in_format_data(self, fieldname):
  777. """Returns True if shown via Print Format::`format_data` property.
  778. Called from within standard print format."""
  779. doc = getattr(self, "parent_doc", self)
  780. if hasattr(doc, "format_data_map"):
  781. return fieldname in doc.format_data_map
  782. else:
  783. return True
  784. def reset_values_if_no_permlevel_access(self, has_access_to, high_permlevel_fields):
  785. """If the user does not have permissions at permlevel > 0, then reset the values to original / default"""
  786. to_reset = []
  787. for df in high_permlevel_fields:
  788. if df.permlevel not in has_access_to and df.fieldtype not in display_fieldtypes:
  789. to_reset.append(df)
  790. if to_reset:
  791. if self.is_new():
  792. # if new, set default value
  793. ref_doc = frappe.new_doc(self.doctype)
  794. else:
  795. # get values from old doc
  796. if self.get('parent_doc'):
  797. parent_doc = self.parent_doc.get_latest()
  798. ref_doc = [d for d in parent_doc.get(self.parentfield) if d.name == self.name][0]
  799. else:
  800. ref_doc = self.get_latest()
  801. for df in to_reset:
  802. self.set(df.fieldname, ref_doc.get(df.fieldname))
  803. def get_value(self, fieldname):
  804. df = self.meta.get_field(fieldname)
  805. val = self.get(fieldname)
  806. return self.cast(val, df)
  807. def cast(self, value, df):
  808. return cast_fieldtype(df.fieldtype, value, show_warning=False)
  809. def _extract_images_from_text_editor(self):
  810. from frappe.core.doctype.file.file import extract_images_from_doc
  811. if self.doctype != "DocType":
  812. for df in self.meta.get("fields", {"fieldtype": ('=', "Text Editor")}):
  813. extract_images_from_doc(self, df.fieldname)
  814. def _filter(data, filters, limit=None):
  815. """pass filters as:
  816. {"key": "val", "key": ["!=", "val"],
  817. "key": ["in", "val"], "key": ["not in", "val"], "key": "^val",
  818. "key" : True (exists), "key": False (does not exist) }"""
  819. out, _filters = [], {}
  820. if not data:
  821. return out
  822. # setup filters as tuples
  823. if filters:
  824. for f in filters:
  825. fval = filters[f]
  826. if not isinstance(fval, (tuple, list)):
  827. if fval is True:
  828. fval = ("not None", fval)
  829. elif fval is False:
  830. fval = ("None", fval)
  831. elif isinstance(fval, str) and fval.startswith("^"):
  832. fval = ("^", fval[1:])
  833. else:
  834. fval = ("=", fval)
  835. _filters[f] = fval
  836. for d in data:
  837. for f, fval in _filters.items():
  838. if not frappe.compare(getattr(d, f, None), fval[0], fval[1]):
  839. break
  840. else:
  841. out.append(d)
  842. if limit and len(out) >= limit:
  843. break
  844. return out