Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 
 
 

684 řádky
20 KiB

  1. """
  2. Contains the Document class representing an object / record
  3. """
  4. import webnotes
  5. import webnotes.model.meta
  6. from webnotes.utils import *
  7. # actually should be "BaseDocType" - deprecated. Only for v160
  8. class SuperDocType:
  9. def __init__(self):
  10. pass
  11. def __getattr__(self, name):
  12. if self.__dict__.has_key(name):
  13. return self.__dict__[name]
  14. elif self.super and hasattr(self.super, name):
  15. return getattr(self.super, name)
  16. else:
  17. raise AttributeError, 'BaseDocType Attribute Error'
  18. class Document:
  19. """
  20. The wn(meta-data)framework equivalent of a Database Record.
  21. Stores,Retrieves,Updates the record in the corresponding table.
  22. Runs the triggers required.
  23. The `Document` class represents the basic Object-Relational Mapper (ORM). The object type is defined by
  24. `DocType` and the object ID is represented by `name`::
  25. Please note the anamoly in the Web Notes Framework that `ID` is always called as `name`
  26. If both `doctype` and `name` are specified in the constructor, then the object is loaded from the database.
  27. If only `doctype` is given, then the object is not loaded
  28. If `fielddata` is specfied, then the object is created from the given dictionary.
  29. **Note 1:**
  30. The getter and setter of the object are overloaded to map to the fields of the object that
  31. are loaded when it is instantiated.
  32. For example: doc.name will be the `name` field and doc.owner will be the `owner` field
  33. **Note 2 - Standard Fields:**
  34. * `name`: ID / primary key
  35. * `owner`: creator of the record
  36. * `creation`: datetime of creation
  37. * `modified`: datetime of last modification
  38. * `modified_by` : last updating user
  39. * `docstatus` : Status 0 - Saved, 1 - Submitted, 2- Cancelled
  40. * `parent` : if child (table) record, this represents the parent record
  41. * `parenttype` : type of parent record (if any)
  42. * `parentfield` : table fieldname of parent record (if any)
  43. * `idx` : Index (sequence) of the child record
  44. """
  45. def __init__(self, doctype = '', name = '', fielddata = {}, prefix='tab'):
  46. self._roles = []
  47. self._perms = []
  48. self._user_defaults = {}
  49. self._prefix = prefix
  50. if fielddata:
  51. self.fields = fielddata
  52. else:
  53. self.fields = {}
  54. if not self.fields.has_key('name'):
  55. self.fields['name']='' # required on save
  56. if not self.fields.has_key('doctype'):
  57. self.fields['doctype']='' # required on save
  58. if not self.fields.has_key('owner'):
  59. self.fields['owner']='' # required on save
  60. if doctype:
  61. self.fields['doctype'] = doctype
  62. if name:
  63. self.fields['name'] = name
  64. self.__initialized = 1
  65. if (doctype and name):
  66. self._loadfromdb(doctype, name)
  67. def __nonzero__(self):
  68. return True
  69. def __str__(self):
  70. return str(self.fields)
  71. # Load Document
  72. # ---------------------------------------------------------------------------
  73. def _loadfromdb(self, doctype = None, name = None):
  74. if name: self.name = name
  75. if doctype: self.doctype = doctype
  76. if webnotes.model.meta.is_single(self.doctype):
  77. self._loadsingle()
  78. else:
  79. dataset = webnotes.conn.sql('select * from `%s%s` where name="%s"' % (self._prefix, self.doctype, self.name.replace('"', '\"')))
  80. if not dataset:
  81. webnotes.msgprint('%s %s does not exist' % (self.doctype, self.name), 1)
  82. raise Exception, '[WNF] %s %s does not exist' % (self.doctype, self.name)
  83. self._load_values(dataset[0], webnotes.conn.get_description())
  84. # Load Fields from dataset
  85. # ---------------------------------------------------------------------------
  86. def _load_values(self, data, description):
  87. for i in range(len(description)):
  88. v = data[i]
  89. self.fields[description[i][0]] = webnotes.conn.convert_to_simple_type(v)
  90. def _merge_values(self, data, description):
  91. for i in range(len(description)):
  92. v = data[i]
  93. if v: # only if value, over-write
  94. self.fields[description[i][0]] = webnotes.conn.convert_to_simple_type(v)
  95. # Load Single Type
  96. # ---------------------------------------------------------------------------
  97. def _loadsingle(self):
  98. self.name = self.doctype
  99. dataset = webnotes.conn.sql("select field, value from tabSingles where doctype='%s'" % self.doctype)
  100. for d in dataset: self.fields[d[0]] = d[1]
  101. # Setter
  102. # ---------------------------------------------------------------------------
  103. def __setattr__(self, name, value):
  104. # normal attribute
  105. if not self.__dict__.has_key('_Document__initialized'):
  106. self.__dict__[name] = value
  107. elif self.__dict__.has_key(name):
  108. self.__dict__[name] = value
  109. else:
  110. # field attribute
  111. f = self.__dict__['fields']
  112. f[name] = value
  113. # Getter
  114. # ---------------------------------------------------------------------------
  115. def __getattr__(self, name):
  116. if self.__dict__.has_key(name):
  117. return self.__dict__[name]
  118. elif self.fields.has_key(name):
  119. return self.fields[name]
  120. else:
  121. return ''
  122. # Get Amendement number
  123. # ---------------------------------------------------------------------------
  124. def _get_amended_name(self):
  125. am_id = 1
  126. am_prefix = self.amended_from
  127. if webnotes.conn.sql('select amended_from from `tab%s` where name = "%s"' % (self.doctype, self.amended_from))[0][0] or '':
  128. am_id = cint(self.amended_from.split('-')[-1]) + 1
  129. am_prefix = '-'.join(self.amended_from.split('-')[:-1]) # except the last hyphen
  130. self.name = am_prefix + '-' + str(am_id)
  131. # Set Name
  132. # ---------------------------------------------------------------------------
  133. def _set_name(self, autoname, istable):
  134. self.localname = self.name
  135. # get my object
  136. import webnotes.model.code
  137. so = webnotes.model.code.get_server_obj(self, [])
  138. # amendments
  139. if self.amended_from:
  140. self._get_amended_name()
  141. # by method
  142. elif so and hasattr(so, 'autoname'):
  143. r = webnotes.model.code.run_server_obj(so, 'autoname')
  144. if r: return r
  145. # based on a field
  146. elif autoname and autoname.startswith('field:'):
  147. n = self.fields[autoname[6:]]
  148. if not n:
  149. raise Exception, 'Name is required'
  150. self.name = n.strip()
  151. # based on expression
  152. elif autoname and autoname.startswith('eval:'):
  153. doc = self # for setting
  154. self.name = eval(autoname[5:])
  155. # call the method!
  156. elif autoname and autoname!='Prompt':
  157. self.name = make_autoname(autoname, self.doctype)
  158. # given
  159. elif self.fields.get('__newname',''):
  160. self.name = self.fields['__newname']
  161. # default name for table
  162. elif istable:
  163. self.name = make_autoname('#########', self.doctype)
  164. # unable to determine a name, use a serial number!
  165. if not self.name:
  166. self.name = make_autoname('#########', self.doctype)
  167. # Validate Name
  168. # ---------------------------------------------------------------------------
  169. def _validate_name(self, case):
  170. if webnotes.conn.sql('select name from `tab%s` where name=%s' % (self.doctype,'%s'), self.name):
  171. raise NameError, 'Name %s already exists' % self.name
  172. # no name
  173. if not self.name: return 'No Name Specified for %s' % self.doctype
  174. # new..
  175. if self.name.startswith('New '+self.doctype):
  176. return 'There were some errors setting the name, please contact the administrator'
  177. if case=='Title Case': self.name = self.name.title()
  178. if case=='UPPER CASE': self.name = self.name.upper()
  179. self.name = self.name.strip() # no leading and trailing blanks
  180. forbidden = ['%', "'", '"', '#', '*', '?', '`']
  181. for f in forbidden:
  182. if f in self.name:
  183. webnotes.msgprint('%s not allowed in ID (name)' % f, raise_exception =1)
  184. # Insert
  185. # ---------------------------------------------------------------------------
  186. def _makenew(self, autoname, istable, case='', make_autoname=1):
  187. # set name
  188. if make_autoname:
  189. self._set_name(autoname, istable)
  190. # validate name
  191. self._validate_name(case)
  192. # insert!
  193. if not self.owner: self.owner = webnotes.session['user']
  194. self.modified_by = webnotes.session['user']
  195. self.creation = self.modified = now()
  196. webnotes.conn.sql("""insert into `tab%(doctype)s` (name, owner, creation, modified, modified_by)
  197. values ('%(name)s', '%(owner)s', '%(creation)s', '%(modified)s', '%(modified_by)s')""" % self.fields)
  198. # Update Values
  199. # ---------------------------------------------------------------------------
  200. def _update_single(self, link_list):
  201. update_str = ["(%s, 'modified', %s)",]
  202. values = [self.doctype, now()]
  203. webnotes.conn.sql("delete from tabSingles where doctype='%s'" % self.doctype)
  204. for f in self.fields.keys():
  205. if not (f in ('modified', 'doctype', 'name', 'perm', 'localname', 'creation'))\
  206. and (not f.startswith('__')): # fields not saved
  207. # validate links
  208. if link_list and link_list.get(f):
  209. self.fields[f] = self._validate_link(link_list[f][0], self.fields[f])
  210. if self.fields[f]==None:
  211. update_str.append("(%s,%s,NULL)")
  212. values.append(self.doctype)
  213. values.append(f)
  214. else:
  215. update_str.append("(%s,%s,%s)")
  216. values.append(self.doctype)
  217. values.append(f)
  218. values.append(self.fields[f])
  219. webnotes.conn.sql("insert into tabSingles(doctype, field, value) values %s" % (', '.join(update_str)), values)
  220. # Validate Links
  221. # ---------------------------------------------------------------------------
  222. def validate_links(self, link_list):
  223. err_list = []
  224. for f in self.fields.keys():
  225. # validate links
  226. old_val = self.fields[f]
  227. if link_list and link_list.get(f):
  228. self.fields[f] = self._validate_link(link_list[f][0], self.fields[f])
  229. if old_val and not self.fields[f]:
  230. s = link_list[f][1] + ': ' + old_val
  231. err_list.append(s)
  232. return err_list
  233. def make_link_list(self):
  234. res = webnotes.model.meta.get_link_fields(self.doctype)
  235. link_list = {}
  236. for i in res: link_list[i[0]] = (i[1], i[2]) # options, label
  237. return link_list
  238. def _validate_link(self, dt, dn):
  239. if not dt: return dn
  240. if not dn: return None
  241. if dt.lower().startswith('link:'):
  242. dt = dt[5:]
  243. if '\n' in dt:
  244. dt = dt.split('\n')[0]
  245. tmp = webnotes.conn.sql("""SELECT name FROM `tab%s` WHERE name = %s""" % (dt, '%s'), dn)
  246. return tmp and tmp[0][0] or ''# match case
  247. # Update query
  248. # ---------------------------------------------------------------------------
  249. def _update_values(self, issingle, link_list, ignore_fields=0):
  250. if issingle:
  251. self._update_single(link_list)
  252. else:
  253. update_str, values = [], []
  254. # set modified timestamp
  255. self.modified = now()
  256. self.modified_by = webnotes.session['user']
  257. for f in self.fields.keys():
  258. if (not (f in ('doctype', 'name', 'perm', 'localname', 'creation','_user_tags'))) \
  259. and (not f.startswith('__')): # fields not saved
  260. # validate links
  261. if link_list and link_list.get(f):
  262. self.fields[f] = self._validate_link(link_list[f][0], self.fields[f])
  263. if self.fields[f]==None or self.fields[f]=='':
  264. update_str.append("`%s`=NULL" % f)
  265. if ignore_fields:
  266. try: r = webnotes.conn.sql("update `tab%s` set `%s`=NULL where name=%s" % (self.doctype, f, '%s'), self.name)
  267. except: pass
  268. else:
  269. values.append(self.fields[f])
  270. update_str.append("`%s`=%s" % (f, '%s'))
  271. if ignore_fields:
  272. try: r = webnotes.conn.sql("update `tab%s` set `%s`=%s where name=%s" % (self.doctype, f, '%s', '%s'), (self.fields[f], self.name))
  273. except: pass
  274. if values:
  275. if not ignore_fields:
  276. # update all in one query
  277. r = webnotes.conn.sql("update `tab%s` set %s where name='%s'" % (self.doctype, ', '.join(update_str), self.name), values)
  278. # Save values
  279. # ---------------------------------------------------------------------------
  280. def save(self, new=0, check_links=1, ignore_fields=0, make_autoname = 1):
  281. """
  282. Saves the current record in the database. If new = 1, creates a new instance of the record.
  283. Also clears temperory fields starting with `__`
  284. * if check_links is set, it validates all `Link` fields
  285. * if ignore_fields is sets, it does not throw an exception for any field that does not exist in the
  286. database table
  287. """
  288. res = webnotes.model.meta.get_dt_values(self.doctype, 'autoname, issingle, istable, name_case', as_dict=1)
  289. res = res and res[0] or {}
  290. # if required, make new
  291. if new or (not new and self.fields.get('__islocal')) and (not res.get('issingle')):
  292. r = self._makenew(res.get('autoname'), res.get('istable'), res.get('name_case'), make_autoname)
  293. if r:
  294. return r
  295. # save the values
  296. self._update_values(res.get('issingle'), check_links and self.make_link_list() or {}, ignore_fields)
  297. self._clear_temp_fields()
  298. # check permissions
  299. # ---------------------------------------------------------------------------
  300. def _get_perms(self):
  301. if not self._perms:
  302. self._perms = webnotes.conn.sql("select role, `match` from tabDocPerm where parent=%s and ifnull(`read`,0) = 1 and ifnull(permlevel,0)=0", self.doctype)
  303. def _get_roles(self):
  304. # check if roles match/
  305. if not self._roles:
  306. if webnotes.user:
  307. self._roles = webnotes.user.get_roles()
  308. else:
  309. self._roles = ['Guest']
  310. def _get_user_defaults(self):
  311. if not self._user_defaults:
  312. if webnotes.user:
  313. self._user_defaults = webnotes.user.get_defaults()
  314. else:
  315. self.defaults = {}
  316. def check_perm(self, verbose=0):
  317. import webnotes
  318. # Admin has all permissions
  319. if webnotes.session['user']=='Administrator':
  320. return 1
  321. # find roles with read access for this record at 0
  322. self._get_perms()
  323. self._get_roles()
  324. self._get_user_defaults()
  325. has_perm, match = 0, []
  326. # loop through everything to find if there is a match
  327. for r in self._perms:
  328. if r[0] in self._roles:
  329. has_perm = 1
  330. if r[1] and match != -1:
  331. match.append(r[1]) # add to match check
  332. else:
  333. match = -1 # has permission and no match, so match not required!
  334. if has_perm and match and match != -1:
  335. for m in match:
  336. if self.fields.get(m, 'no value') in self._user_defaults.get(m, 'no default'):
  337. has_perm = 1
  338. break # permission found! break
  339. else:
  340. has_perm = 0
  341. if verbose:
  342. webnotes.msgprint("Value not allowed: '%s' for '%s'" % (self.fields.get(m, 'no value'), m))
  343. # check for access key
  344. if webnotes.form and webnotes.form.has_key('akey'):
  345. import webnotes.utils.encrypt
  346. if webnotes.utils.encrypt.decrypt(webnotes.form.getvalue('akey')) == self.name:
  347. has_perm = 1
  348. webnotes.response['print_access'] = 1
  349. return has_perm
  350. # Cleanup
  351. # ---------------------------------------------------------------------------
  352. def _clear_temp_fields(self):
  353. # clear temp stuff
  354. keys = self.fields.keys()
  355. for f in keys:
  356. if f.startswith('__'):
  357. del self.fields[f]
  358. # Table methods
  359. # ---------------------------------------------------------------------------
  360. def clear_table(self, doclist, tablefield, save=0):
  361. """
  362. Clears the child records from the given `doclist` for a particular `tablefield`
  363. """
  364. from webnotes.model.utils import getlist
  365. for d in getlist(doclist, tablefield):
  366. d.fields['__oldparent'] = d.parent
  367. d.parent = 'old_parent:' + d.parent # for client to send it back while saving
  368. d.docstatus = 2
  369. if save and not d.fields.get('__islocal'):
  370. d.save()
  371. self.fields['__unsaved'] = 1
  372. def addchild(self, fieldname, childtype = '', local=0, doclist=None):
  373. """
  374. Returns a child record of the give `childtype`.
  375. * if local is set, it does not save the record
  376. * if doclist is passed, it append the record to the doclist
  377. """
  378. if not childtype:
  379. childtype = db_getchildtype(self.doctype, fieldname)
  380. d = Document()
  381. d.parent = self.name
  382. d.parenttype = self.doctype
  383. d.parentfield = fieldname
  384. d.doctype = childtype
  385. d.docstatus = 0;
  386. d.name = ''
  387. d.owner = webnotes.session['user']
  388. if local:
  389. d.fields['__islocal'] = '1' # for Client to identify unsaved doc
  390. else:
  391. d.save(new=1)
  392. if doclist != None:
  393. doclist.append(d)
  394. return d
  395. def addchild(parent, fieldname, childtype = '', local=0, doclist=None):
  396. """
  397. Create a child record to the parent doc.
  398. Example::
  399. c = Document('Contact','ABC')
  400. d = addchild(c, 'contact_updates', 'Contact Update', local = 1)
  401. d.last_updated = 'Phone call'
  402. d.save(1)
  403. """
  404. return parent.addchild(fieldname, childtype, local, doclist)
  405. # Remove Child
  406. # ------------
  407. def removechild(d, is_local = 0):
  408. """
  409. Sets the docstatus of the object d to 2 (deleted) and appends an 'old_parent:' to the parent name
  410. """
  411. if not is_local:
  412. set(d, 'docstatus', 2)
  413. set(d, 'parent', 'old_parent:' + d.parent)
  414. else:
  415. d.parent = 'old_parent:' + d.parent
  416. d.docstatus = 2
  417. # Naming
  418. # ------
  419. def make_autoname(key, doctype=''):
  420. """
  421. Creates an autoname from the given key:
  422. **Autoname rules:**
  423. * The key is separated by '.'
  424. * '####' represents a series. The string before this part becomes the prefix:
  425. Example: ABC.#### creates a series ABC0001, ABC0002 etc
  426. * 'MM' represents the current month
  427. * 'YY' and 'YYYY' represent the current year
  428. *Example:*
  429. * DE/./.YY./.MM./.##### will create a series like
  430. DE/09/01/0001 where 09 is the year, 01 is the month and 0001 is the series
  431. """
  432. n = ''
  433. l = key.split('.')
  434. for e in l:
  435. en = ''
  436. if e.startswith('#'):
  437. digits = len(e)
  438. en = getseries(n, digits, doctype)
  439. elif e=='YY':
  440. import time
  441. en = time.strftime('%y')
  442. elif e=='MM':
  443. import time
  444. en = time.strftime('%m')
  445. elif e=='YYYY':
  446. import time
  447. en = time.strftime('%Y')
  448. else: en = e
  449. n+=en
  450. return n
  451. # Get Series for Autoname
  452. # -----------------------
  453. def getseries(key, digits, doctype=''):
  454. # series created ?
  455. if webnotes.conn.sql("select name from tabSeries where name='%s'" % key):
  456. # yes, update it
  457. webnotes.conn.sql("update tabSeries set current = current+1 where name='%s'" % key)
  458. # find the series counter
  459. r = webnotes.conn.sql("select current from tabSeries where name='%s'" % key)
  460. n = r[0][0]
  461. else:
  462. # no, create it
  463. webnotes.conn.sql("insert into tabSeries (name, current) values ('%s', 1)" % key)
  464. n = 1
  465. return ('%0'+str(digits)+'d') % n
  466. # Get Children
  467. # ------------
  468. def getchildren(name, childtype, field='', parenttype='', from_doctype=0, prefix='tab'):
  469. import webnotes
  470. tmp = ''
  471. if field:
  472. tmp = ' and parentfield="%s" ' % field
  473. if parenttype:
  474. tmp = ' and parenttype="%s" ' % parenttype
  475. try:
  476. dataset = webnotes.conn.sql("select * from `%s%s` where parent='%s' %s order by idx" % (prefix, childtype, name, tmp))
  477. desc = webnotes.conn.get_description()
  478. except Exception, e:
  479. if prefix=='arc' and e.args[0]==1146:
  480. return []
  481. else:
  482. raise e
  483. l = []
  484. for i in dataset:
  485. d = Document()
  486. d.doctype = childtype
  487. d._load_values(i, desc)
  488. l.append(d)
  489. return l
  490. # Check if "Guest" is allowed to view this page
  491. # ---------------------------------------------
  492. def check_page_perm(doc):
  493. if doc.name=='Login Page':
  494. return
  495. if doc.publish:
  496. return
  497. if not webnotes.conn.sql("select name from `tabPage Role` where parent=%s and role='Guest'", doc.name):
  498. webnotes.response['exc_type'] = 'PermissionError'
  499. raise Exception, '[WNF] No read permission for %s %s' % ('Page', doc.name)
  500. def get_report_builder_code(doc):
  501. if doc.doctype=='Search Criteria':
  502. from webnotes.model.code import get_code
  503. if doc.standard != 'No':
  504. doc.report_script = get_code(doc.module, 'Search Criteria', doc.name, 'js')
  505. doc.custom_query = get_code(doc.module, 'Search Criteria', doc.name, 'sql')
  506. # called from everywhere
  507. # load a record and its child records and bundle it in a list - doclist
  508. # ---------------------------------------------------------------------
  509. def get(dt, dn='', with_children = 1, from_get_obj = 0, prefix = 'tab'):
  510. """
  511. Returns a doclist containing the main record and all child records
  512. """
  513. import webnotes
  514. import webnotes.model
  515. import webnotes.defs
  516. dn = dn or dt
  517. # load the main doc
  518. doc = Document(dt, dn, prefix=prefix)
  519. # check permission - for doctypes, pages
  520. if (dt in ('DocType', 'Page', 'Control Panel', 'Search Criteria')) or (from_get_obj and webnotes.session.get('user') != 'Guest'):
  521. if dt=='Page' and webnotes.session['user'] == 'Guest':
  522. check_page_perm(doc)
  523. else:
  524. if not doc.check_perm():
  525. webnotes.response['exc_type'] = 'PermissionError'
  526. raise webnotes.ValidationError, '[WNF] No read permission for %s %s' % (dt, dn)
  527. # import report_builder code
  528. get_report_builder_code(doc)
  529. if not with_children:
  530. # done
  531. return [doc,]
  532. # get all children types
  533. tablefields = webnotes.model.meta.get_table_fields(dt)
  534. # load chilren
  535. doclist = [doc,]
  536. for t in tablefields:
  537. doclist += getchildren(doc.name, t[0], t[1], dt, prefix=prefix)
  538. return doclist