Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 
 
 
 

749 rader
21 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. """
  5. Contains the Document class representing an object / record
  6. """
  7. _toc = ["frappe.model.doc.Document"]
  8. import frappe
  9. import frappe.model.meta
  10. from frappe.utils import *
  11. class Document:
  12. """
  13. The frappe(meta-data)framework equivalent of a Database Record.
  14. Stores,Retrieves,Updates the record in the corresponding table.
  15. Runs the triggers required.
  16. The `Document` class represents the basic Object-Relational Mapper (ORM). The object type is defined by
  17. `DocType` and the object ID is represented by `name`::
  18. Please note the anamoly in the Web Notes Framework that `ID` is always called as `name`
  19. If both `doctype` and `name` are specified in the constructor, then the object is loaded from the database.
  20. If only `doctype` is given, then the object is not loaded
  21. If `fielddata` is specfied, then the object is created from the given dictionary.
  22. **Note 1:**
  23. The getter and setter of the object are overloaded to map to the fields of the object that
  24. are loaded when it is instantiated.
  25. For example: doc.name will be the `name` field and doc.owner will be the `owner` field
  26. **Note 2 - Standard Fields:**
  27. * `name`: ID / primary key
  28. * `owner`: creator of the record
  29. * `creation`: datetime of creation
  30. * `modified`: datetime of last modification
  31. * `modified_by` : last updating user
  32. * `docstatus` : Status 0 - Saved, 1 - Submitted, 2- Cancelled
  33. * `parent` : if child (table) record, this represents the parent record
  34. * `parenttype` : type of parent record (if any)
  35. * `parentfield` : table fieldname of parent record (if any)
  36. * `idx` : Index (sequence) of the child record
  37. """
  38. def __init__(self, doctype = None, name = None, fielddata = None):
  39. self._roles = []
  40. self._perms = []
  41. self._user_defaults = {}
  42. self._new_name_set = False
  43. self._meta = None
  44. if isinstance(doctype, dict):
  45. fielddata = doctype
  46. doctype = None
  47. if doctype and isinstance(name, dict):
  48. name = frappe.db.get_value(doctype, name, "name") or None
  49. if fielddata:
  50. self.fields = frappe._dict(fielddata)
  51. else:
  52. self.fields = frappe._dict()
  53. if not self.fields.has_key('name'):
  54. self.fields['name']='' # required on save
  55. if not self.fields.has_key('doctype'):
  56. self.fields['doctype']='' # required on save
  57. if not self.fields.has_key('owner'):
  58. self.fields['owner']='' # required on save
  59. if doctype:
  60. self.fields['doctype'] = doctype
  61. if name:
  62. self.fields['name'] = name
  63. self.__initialized = 1
  64. if (doctype and name):
  65. self._loadfromdb(doctype, name)
  66. else:
  67. if not fielddata:
  68. self.fields['__islocal'] = 1
  69. if not self.fields.docstatus:
  70. self.fields.docstatus = 0
  71. def __nonzero__(self):
  72. return True
  73. def __str__(self):
  74. return str(self.fields)
  75. def __repr__(self):
  76. return repr(self.fields)
  77. def __unicode__(self):
  78. return unicode(self.fields)
  79. def __eq__(self, other):
  80. if isinstance(other, Document):
  81. return self.fields == other.fields
  82. else:
  83. return False
  84. def __getstate__(self):
  85. return self.fields
  86. def __setstate__(self, d):
  87. self.fields = d
  88. def encode(self, encoding='utf-8'):
  89. """convert all unicode values to utf-8"""
  90. from frappe.utils import encode_dict
  91. encode_dict(self.fields)
  92. def _loadfromdb(self, doctype = None, name = None):
  93. if name: self.name = name
  94. if doctype: self.doctype = doctype
  95. is_single = False
  96. try:
  97. is_single = frappe.model.meta.is_single(self.doctype)
  98. except Exception, e:
  99. pass
  100. if is_single:
  101. self._loadsingle()
  102. else:
  103. try:
  104. dataset = frappe.db.sql('select * from `tab%s` where name="%s"' % (self.doctype, self.name.replace('"', '\"')))
  105. except frappe.SQLError, e:
  106. if e.args[0]==1146:
  107. dataset = None
  108. else:
  109. raise
  110. if not dataset:
  111. raise frappe.DoesNotExistError, '[WNF] %s %s does not exist' % (self.doctype, self.name)
  112. self._load_values(dataset[0], frappe.db.get_description())
  113. def is_new(self):
  114. return self.fields.get("__islocal")
  115. def _load_values(self, data, description):
  116. if '__islocal' in self.fields:
  117. del self.fields['__islocal']
  118. for i in range(len(description)):
  119. v = data[i]
  120. self.fields[description[i][0]] = frappe.db.convert_to_simple_type(v)
  121. def _merge_values(self, data, description):
  122. for i in range(len(description)):
  123. v = data[i]
  124. if v: # only if value, over-write
  125. self.fields[description[i][0]] = frappe.db.convert_to_simple_type(v)
  126. def _loadsingle(self):
  127. self.name = self.doctype
  128. self.fields.update(getsingle(self.doctype))
  129. def __setattr__(self, name, value):
  130. # normal attribute
  131. if not self.__dict__.has_key('_Document__initialized'):
  132. self.__dict__[name] = value
  133. elif self.__dict__.has_key(name):
  134. self.__dict__[name] = value
  135. else:
  136. # field attribute
  137. f = self.__dict__['fields']
  138. f[name] = value
  139. def __getattr__(self, name):
  140. if self.__dict__.has_key(name):
  141. return self.__dict__[name]
  142. elif self.fields.has_key(name):
  143. return self.fields[name]
  144. else:
  145. if name.startswith("__"):
  146. raise AttributeError()
  147. return None
  148. def get(self, name, value=None):
  149. return self.fields.get(name, value)
  150. def update(self, d):
  151. self.fields.update(d)
  152. return self
  153. def update_if_missing(self, d):
  154. fields = self.get_valid_fields()
  155. for key, value in d.iteritems():
  156. if (key in fields) and (not self.fields.get(key)):
  157. self.fields[key] = value
  158. def insert(self):
  159. self.fields['__islocal'] = 1
  160. self.save()
  161. return self
  162. def save(self, new=0, check_links=1, ignore_fields=0, make_autoname=1,
  163. keep_timestamps=False):
  164. self.get_meta()
  165. if new:
  166. self.fields["__islocal"] = 1
  167. # add missing parentinfo (if reqd)
  168. if self.parent and not (self.parenttype and self.parentfield):
  169. self.update_parentinfo()
  170. if self.parent and not self.idx:
  171. self.set_idx()
  172. # if required, make new
  173. if not self._meta.issingle:
  174. if self.is_new():
  175. r = self._insert(make_autoname=make_autoname, keep_timestamps = keep_timestamps)
  176. if r:
  177. return r
  178. else:
  179. if not frappe.db.exists(self.doctype, self.name):
  180. frappe.msgprint(frappe._("Cannot update a non-exiting record, try inserting.") + ": " + self.doctype + " / " + self.name,
  181. raise_exception=1)
  182. # save the values
  183. self._update_values(self._meta.issingle,
  184. check_links and self.make_link_list() or {}, ignore_fields=ignore_fields,
  185. keep_timestamps=keep_timestamps)
  186. self._clear_temp_fields()
  187. def _get_amended_name(self):
  188. am_id = 1
  189. am_prefix = self.amended_from
  190. if frappe.db.sql('select amended_from from `tab%s` where name = "%s"' % (self.doctype, self.amended_from))[0][0] or '':
  191. am_id = cint(self.amended_from.split('-')[-1]) + 1
  192. am_prefix = '-'.join(self.amended_from.split('-')[:-1]) # except the last hyphen
  193. self.name = am_prefix + '-' + str(am_id)
  194. def set_new_name(self, controller=None):
  195. if self._new_name_set:
  196. # already set by bean
  197. return
  198. self._new_name_set = True
  199. self.get_meta()
  200. autoname = self._meta.autoname
  201. self.localname = self.name
  202. # amendments
  203. if self.amended_from:
  204. return self._get_amended_name()
  205. # by method
  206. else:
  207. # get my object
  208. if not controller:
  209. controller = frappe.get_obj([self])
  210. if hasattr(controller, 'autoname'):
  211. return controller.autoname()
  212. # based on a field
  213. if autoname and autoname.startswith('field:'):
  214. n = self.fields[autoname[6:]]
  215. if not n:
  216. raise Exception, 'Name is required'
  217. self.name = n.strip()
  218. elif autoname and autoname.startswith("naming_series:"):
  219. self.set_naming_series()
  220. if not self.naming_series:
  221. frappe.msgprint(frappe._("Naming Series mandatory"), raise_exception=True)
  222. self.name = make_autoname(self.naming_series+'.#####')
  223. # call the method!
  224. elif autoname and autoname!='Prompt':
  225. self.name = make_autoname(autoname, self.doctype)
  226. # given
  227. elif self.fields.get('__newname',''):
  228. self.name = self.fields['__newname']
  229. # default name for table
  230. elif self._meta.istable:
  231. self.name = make_autoname('#########', self.doctype)
  232. # unable to determine a name, use global series
  233. if not self.name:
  234. self.name = make_autoname('#########', self.doctype)
  235. def set_naming_series(self):
  236. if not self.naming_series:
  237. # pick default naming series
  238. self.naming_series = get_default_naming_series(self.doctype)
  239. def append_number_if_name_exists(self):
  240. if frappe.db.exists(self.doctype, self.name):
  241. last = frappe.db.sql("""select name from `tab{}`
  242. where name regexp '{}-[[:digit:]]*'
  243. order by name desc limit 1""".format(self.doctype, self.name))
  244. if last:
  245. count = str(cint(last[0][0].rsplit("-", 1)[1]) + 1)
  246. else:
  247. count = "1"
  248. self.name = "{0}-{1}".format(self.name, count)
  249. def set(self, key, value):
  250. self.modified = now()
  251. self.modified_by = frappe.session["user"]
  252. frappe.db.set_value(self.doctype, self.name, key, value, self.modified, self.modified_by)
  253. self.fields[key] = value
  254. def _insert(self, make_autoname=True, keep_timestamps=False):
  255. # set name
  256. if make_autoname:
  257. self.set_new_name()
  258. # validate name
  259. self.name = validate_name(self.doctype, self.name, self._meta.name_case)
  260. # insert!
  261. if not keep_timestamps:
  262. if not self.owner:
  263. self.owner = frappe.session['user']
  264. self.modified_by = frappe.session['user']
  265. if not self.creation:
  266. self.creation = self.modified = now()
  267. else:
  268. self.modified = now()
  269. frappe.db.sql("insert into `tab%(doctype)s`" % self.fields \
  270. + """ (name, owner, creation, modified, modified_by)
  271. values (%(name)s, %(owner)s, %(creation)s, %(modified)s,
  272. %(modified_by)s)""", self.fields)
  273. def _update_single(self, link_list):
  274. self.modified = now()
  275. update_str, values = [], []
  276. frappe.db.sql("delete from tabSingles where doctype='%s'" % self.doctype)
  277. for f in self.fields.keys():
  278. if not (f in ('modified', 'doctype', 'name', 'perm', 'localname', 'creation'))\
  279. and (not f.startswith('__')): # fields not saved
  280. # validate links
  281. if link_list and link_list.get(f):
  282. self.fields[f] = self._validate_link(link_list, f)
  283. if self.fields[f]==None:
  284. update_str.append("(%s,%s,NULL)")
  285. values.append(self.doctype)
  286. values.append(f)
  287. else:
  288. update_str.append("(%s,%s,%s)")
  289. values.append(self.doctype)
  290. values.append(f)
  291. values.append(self.fields[f])
  292. frappe.db.sql("insert into tabSingles(doctype, field, value) values %s" % (', '.join(update_str)), values)
  293. def validate_links(self, link_list):
  294. err_list = []
  295. for f in self.fields.keys():
  296. # validate links
  297. old_val = self.fields[f]
  298. if link_list and link_list.get(f):
  299. self.fields[f] = self._validate_link(link_list, f)
  300. if old_val and not self.fields[f]:
  301. err_list.append("{}: {}".format(link_list[f][1], old_val))
  302. return err_list
  303. def make_link_list(self):
  304. res = frappe.model.meta.get_link_fields(self.doctype)
  305. link_list = {}
  306. for i in res: link_list[i[0]] = (i[1], i[2]) # options, label
  307. return link_list
  308. def _validate_link(self, link_list, f):
  309. dt = link_list[f][0]
  310. dn = self.fields.get(f)
  311. if not dt:
  312. frappe.throw("Options not set for link field: " + f)
  313. if not dt: return dn
  314. if not dn: return None
  315. if dt=="[Select]": return dn
  316. if dt.lower().startswith('link:'):
  317. dt = dt[5:]
  318. if '\n' in dt:
  319. dt = dt.split('\n')[0]
  320. tmp = frappe.db.sql("""SELECT name FROM `tab%s`
  321. WHERE name = %s""" % (dt, '%s'), (dn,))
  322. return tmp and tmp[0][0] or ''# match case
  323. def _update_values(self, issingle, link_list, ignore_fields=0, keep_timestamps=False):
  324. if issingle:
  325. self._update_single(link_list)
  326. else:
  327. update_str, values = [], []
  328. # set modified timestamp
  329. if self.modified and not keep_timestamps:
  330. self.modified = now()
  331. self.modified_by = frappe.session['user']
  332. fields_list = ignore_fields and self.get_valid_fields() or self.fields.keys()
  333. for f in fields_list:
  334. if (not (f in ('doctype', 'name', 'perm', 'localname',
  335. 'creation','_user_tags', "file_list", "_comments"))) and (not f.startswith('__')):
  336. # fields not saved
  337. # validate links
  338. if link_list and link_list.get(f):
  339. self.fields[f] = self._validate_link(link_list, f)
  340. if self.fields.get(f) is None or self.fields.get(f)=='':
  341. update_str.append("`%s`=NULL" % f)
  342. else:
  343. values.append(self.fields.get(f))
  344. update_str.append("`%s`=%s" % (f, '%s'))
  345. if values:
  346. values.append(self.name)
  347. r = frappe.db.sql("update `tab%s` set %s where name=%s" % \
  348. (self.doctype, ', '.join(update_str), "%s"), values)
  349. def get_valid_fields(self):
  350. import frappe.model.doctype
  351. if getattr(frappe.local, "valid_fields_map", None) is None:
  352. frappe.local.valid_fields_map = {}
  353. self.get_meta()
  354. valid_fields_map = frappe.local.valid_fields_map
  355. if not valid_fields_map.get(self.doctype):
  356. if cint( self._meta.issingle):
  357. doctypelist = frappe.model.doctype.get(self.doctype)
  358. valid_fields_map[self.doctype] = doctypelist.get_fieldnames({
  359. "fieldtype": ["not in", frappe.model.no_value_fields]})
  360. else:
  361. valid_fields_map[self.doctype] = \
  362. frappe.db.get_table_columns(self.doctype)
  363. return valid_fields_map.get(self.doctype)
  364. def get_meta(self):
  365. if not self._meta:
  366. self._meta = frappe.db.get_value("DocType", self.doctype, ["autoname", "issingle",
  367. "istable", "name_case"], as_dict=True) or frappe._dict()
  368. return self._meta
  369. def update_parentinfo(self):
  370. """update parent type and parent field, if not explicitly specified"""
  371. tmp = frappe.db.sql("""select parent, fieldname from tabDocField
  372. where fieldtype='Table' and options=%s""", (self.doctype,))
  373. if len(tmp)==0:
  374. raise Exception, 'Incomplete parent info in child table (%s, %s)' \
  375. % (self.doctype, self.fields.get('name', '[new]'))
  376. elif len(tmp)>1:
  377. raise Exception, 'Ambiguous parent info (%s, %s)' \
  378. % (self.doctype, self.fields.get('name', '[new]'))
  379. else:
  380. self.parenttype = tmp[0][0]
  381. self.parentfield = tmp[0][1]
  382. def set_idx(self):
  383. """set idx"""
  384. self.idx = (frappe.db.sql("""select max(idx) from `tab%s`
  385. where parent=%s and parentfield=%s""" % (self.doctype, '%s', '%s'),
  386. (self.parent, self.parentfield))[0][0] or 0) + 1
  387. def _clear_temp_fields(self):
  388. # clear temp stuff
  389. keys = self.fields.keys()
  390. for f in keys:
  391. if f.startswith('__'):
  392. del self.fields[f]
  393. def clear_table(self, doclist, tablefield, save=0):
  394. """
  395. Clears the child records from the given `doclist` for a particular `tablefield`
  396. """
  397. from frappe.model.utils import getlist
  398. table_list = getlist(doclist, tablefield)
  399. delete_list = [d.name for d in table_list]
  400. if delete_list:
  401. #filter doclist
  402. doclist = filter(lambda d: d.name not in delete_list, doclist)
  403. # delete from db
  404. frappe.db.sql("""\
  405. delete from `tab%s`
  406. where parent=%s and parenttype=%s"""
  407. % (table_list[0].doctype, '%s', '%s'),
  408. (self.name, self.doctype))
  409. self.fields['__unsaved'] = 1
  410. return frappe.doclist(doclist)
  411. def addchild(self, fieldname, childtype = '', doclist=None):
  412. """
  413. Returns a child record of the give `childtype`.
  414. * if local is set, it does not save the record
  415. * if doclist is passed, it append the record to the doclist
  416. """
  417. from frappe.model.doc import Document
  418. d = Document()
  419. d.parent = self.name
  420. d.parenttype = self.doctype
  421. d.parentfield = fieldname
  422. d.doctype = childtype
  423. d.docstatus = 0;
  424. d.name = ''
  425. d.owner = frappe.session['user']
  426. d.fields['__islocal'] = 1 # for Client to identify unsaved doc
  427. if doclist != None:
  428. doclist.append(d)
  429. return d
  430. def get_values(self):
  431. """get non-null fields dict withouth standard fields"""
  432. from frappe.model import default_fields
  433. ret = {}
  434. for key in self.fields:
  435. if key not in default_fields and self.fields[key]:
  436. ret[key] = self.fields[key]
  437. return ret
  438. def addchild(parent, fieldname, childtype = '', doclist=None):
  439. """
  440. Create a child record to the parent doc.
  441. Example::
  442. c = Document('Contact','ABC')
  443. d = addchild(c, 'contact_updates', 'Contact Update')
  444. d.last_updated = 'Phone call'
  445. d.save(1)
  446. """
  447. return parent.addchild(fieldname, childtype, doclist)
  448. def make_autoname(key, doctype=''):
  449. """
  450. Creates an autoname from the given key:
  451. **Autoname rules:**
  452. * The key is separated by '.'
  453. * '####' represents a series. The string before this part becomes the prefix:
  454. Example: ABC.#### creates a series ABC0001, ABC0002 etc
  455. * 'MM' represents the current month
  456. * 'YY' and 'YYYY' represent the current year
  457. *Example:*
  458. * DE/./.YY./.MM./.##### will create a series like
  459. DE/09/01/0001 where 09 is the year, 01 is the month and 0001 is the series
  460. """
  461. if not "#" in key:
  462. key = key + ".#####"
  463. n = ''
  464. l = key.split('.')
  465. series_set = False
  466. today = now_datetime()
  467. for e in l:
  468. en = ''
  469. if e.startswith('#'):
  470. if not series_set:
  471. digits = len(e)
  472. en = getseries(n, digits, doctype)
  473. series_set = True
  474. elif e=='YY':
  475. en = today.strftime('%y')
  476. elif e=='MM':
  477. en = today.strftime('%m')
  478. elif e=='DD':
  479. en = today.strftime("%d")
  480. elif e=='YYYY':
  481. en = today.strftime('%Y')
  482. else: en = e
  483. n+=en
  484. return n
  485. def getseries(key, digits, doctype=''):
  486. # series created ?
  487. current = frappe.db.sql("select `current` from `tabSeries` where name=%s for update", (key,))
  488. if current and current[0][0] is not None:
  489. current = current[0][0]
  490. # yes, update it
  491. frappe.db.sql("update tabSeries set current = current+1 where name=%s", (key,))
  492. current = cint(current) + 1
  493. else:
  494. # no, create it
  495. frappe.db.sql("insert into tabSeries (name, current) values (%s, 1)", (key,))
  496. current = 1
  497. return ('%0'+str(digits)+'d') % current
  498. def getchildren(name, childtype, field='', parenttype='', from_doctype=0):
  499. import frappe
  500. from frappe.model.doclist import DocList
  501. condition = ""
  502. values = []
  503. if field:
  504. condition += ' and parentfield=%s '
  505. values.append(field)
  506. if parenttype:
  507. condition += ' and parenttype=%s '
  508. values.append(parenttype)
  509. dataset = frappe.db.sql("""select * from `tab%s` where parent=%s %s order by idx""" \
  510. % (childtype, "%s", condition), tuple([name]+values))
  511. desc = frappe.db.get_description()
  512. l = DocList()
  513. for i in dataset:
  514. d = Document()
  515. d.doctype = childtype
  516. d._load_values(i, desc)
  517. l.append(d)
  518. return l
  519. def check_page_perm(doc):
  520. if doc.name=='Login Page':
  521. return
  522. if doc.publish:
  523. return
  524. if not frappe.db.sql("select name from `tabPage Role` where parent=%s and role='Guest'", (doc.name,)):
  525. frappe.response['403'] = 1
  526. raise frappe.PermissionError, '[WNF] No read permission for %s %s' % ('Page', doc.name)
  527. def get(dt, dn='', with_children = 1, from_controller = 0):
  528. """
  529. Returns a doclist containing the main record and all child records
  530. """
  531. import frappe
  532. import frappe.model
  533. from frappe.model.doclist import DocList
  534. dn = dn or dt
  535. # load the main doc
  536. doc = Document(dt, dn)
  537. if dt=='Page' and frappe.session['user'] == 'Guest':
  538. check_page_perm(doc)
  539. if not with_children:
  540. # done
  541. return DocList([doc,])
  542. # get all children types
  543. tablefields = frappe.model.meta.get_table_fields(dt)
  544. # load chilren
  545. doclist = DocList([doc,])
  546. for t in tablefields:
  547. doclist += getchildren(doc.name, t[0], t[1], dt)
  548. return doclist
  549. def getsingle(doctype):
  550. """get single doc as dict"""
  551. dataset = frappe.db.sql("select field, value from tabSingles where doctype=%s", (doctype,))
  552. return dict(dataset)
  553. def copy_common_fields(from_doc, to_doc):
  554. from frappe.model import default_fields
  555. doctype_list = frappe.get_doctype(to_doc.doctype)
  556. for fieldname, value in from_doc.fields.items():
  557. if fieldname in default_fields:
  558. continue
  559. if doctype_list.get_field(fieldname) and to_doc.fields[fieldname] != value:
  560. to_doc.fields[fieldname] = value
  561. def validate_name(doctype, name, case=None, merge=False):
  562. if not merge:
  563. if frappe.db.sql('select name from `tab%s` where name=%s' % (doctype,'%s'), (name,)):
  564. raise NameError, 'Name %s already exists' % name
  565. # no name
  566. if not name: return 'No Name Specified for %s' % doctype
  567. # new..
  568. if name.startswith('New '+doctype):
  569. raise NameError, 'There were some errors setting the name, please contact the administrator'
  570. if case=='Title Case': name = name.title()
  571. if case=='UPPER CASE': name = name.upper()
  572. name = name.strip() # no leading and trailing blanks
  573. forbidden = ['%', "'", '"', '#', '*', '?', '`']
  574. for f in forbidden:
  575. if f in name:
  576. frappe.msgprint('%s not allowed in ID (name)' % f, raise_exception =1)
  577. return name
  578. def get_default_naming_series(doctype):
  579. """get default value for `naming_series` property"""
  580. from frappe.model.doctype import get_property
  581. naming_series = get_property(doctype, "options", "naming_series")
  582. if naming_series:
  583. naming_series = naming_series.split("\n")
  584. return naming_series[0] or naming_series[1]
  585. else:
  586. return None