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.

form.js 30 KiB

13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136
  1. // Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com)
  2. //
  3. // MIT License (MIT)
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a
  6. // copy of this software and associated documentation files (the "Software"),
  7. // to deal in the Software without restriction, including without limitation
  8. // the rights to use, copy, modify, merge, publish, distribute, sublicense,
  9. // and/or sell copies of the Software, and to permit persons to whom the
  10. // Software is furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  16. // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  17. // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  18. // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
  19. // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
  20. // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  21. //
  22. /* Form page structure
  23. + this.parent (either FormContainer or Dialog)
  24. + this.wrapper
  25. + this.content
  26. + this.saved_wrapper
  27. + wn.PageLayout (this.page_layout_layout)
  28. + this.wrapper
  29. + this.wtab (table)
  30. + this.main
  31. + this.head
  32. + this.body
  33. + this.layout
  34. + this.footer
  35. + this.sidebar
  36. + this.print_wrapper
  37. + this.head
  38. */
  39. wn.provide('_f');
  40. _f.frms = {};
  41. _f.Frm = function(doctype, parent) {
  42. this.docname = '';
  43. this.doctype = doctype;
  44. this.display = 0;
  45. var me = this;
  46. this.is_editable = {};
  47. this.opendocs = {};
  48. this.sections = [];
  49. this.grids = [];
  50. this.cscript = {};
  51. this.pformat = {};
  52. this.fetch_dict = {};
  53. this.parent = parent;
  54. this.tinymce_id_list = [];
  55. this.setup_meta(doctype);
  56. // notify on rename
  57. var me = this;
  58. $(document).bind('rename', function(event, dt, old_name, new_name) {
  59. if(dt==me.doctype)
  60. me.rename_notify(dt, old_name, new_name)
  61. });
  62. }
  63. // ======================================================================================
  64. _f.Frm.prototype.check_doctype_conflict = function(docname) {
  65. var me = this;
  66. if(this.doctype=='DocType' && docname=='DocType') {
  67. msgprint('Allowing DocType, DocType. Be careful!')
  68. } else if(this.doctype=='DocType') {
  69. if(wn.views.formview[docname]) {
  70. msgprint("Cannot open DocType when its instance is open")
  71. throw 'doctype open conflict'
  72. }
  73. } else {
  74. if(wn.views.formview.DocType && wn.views.formview.DocType.frm.opendocs[this.doctype]) {
  75. msgprint("Cannot open instance when its DocType is open")
  76. throw 'doctype open conflict'
  77. }
  78. }
  79. }
  80. _f.Frm.prototype.setup = function() {
  81. var me = this;
  82. this.fields = [];
  83. this.fields_dict = {};
  84. // wrapper
  85. this.wrapper = this.parent;
  86. // create area for print fomrat
  87. this.setup_print_layout();
  88. // thank you goes here (in case of Guest, don't refresh, just say thank you!)
  89. this.saved_wrapper = $a(this.wrapper, 'div');
  90. // 2 column layout
  91. this.setup_std_layout();
  92. // client script must be called after "setup" - there are no fields_dict attached to the frm otherwise
  93. this.setup_client_script();
  94. this.setup_done = true;
  95. }
  96. // ======================================================================================
  97. _f.Frm.prototype.setup_print_layout = function() {
  98. this.print_wrapper = $a(this.wrapper, 'div');
  99. this.print_head = $a(this.print_wrapper, 'div');
  100. this.print_body = $a(this.print_wrapper,'div', 'layout_wrapper', {
  101. padding:'23px',
  102. minHeight: '800px'
  103. });
  104. var t= make_table(this.print_head, 1 ,2, '100%', [], {padding: '6px'});
  105. this.view_btn_wrapper = $a($td(t,0,0) , 'span', 'green_buttons');
  106. this.view_btn = $btn(this.view_btn_wrapper, 'View Details', function() { cur_frm.edit_doc() },
  107. {marginRight:'4px'}, 'green');
  108. this.print_btn = $btn($td(t,0,0), 'Print', function() { cur_frm.print_doc() });
  109. $y($td(t,0,1), {textAlign: 'right'});
  110. this.print_close_btn = $btn($td(t,0,1), 'Close', function() { window.back(); });
  111. }
  112. _f.Frm.prototype.onhide = function() { if(_f.cur_grid_cell) _f.cur_grid_cell.grid.cell_deselect(); }
  113. // ======================================================================================
  114. _f.Frm.prototype.setup_std_layout = function() {
  115. this.page_layout = new wn.PageLayout({
  116. parent: this.wrapper,
  117. main_width: this.meta.in_dialog ? '100%' : '75%',
  118. sidebar_width: this.meta.in_dialog ? '0%' : '25%'
  119. })
  120. // only tray
  121. this.meta.section_style='Simple'; // always simple!
  122. // layout
  123. this.layout = new Layout(this.page_layout.body, '100%');
  124. // sidebar
  125. if(this.meta.in_dialog) {
  126. // hide sidebar
  127. $(this.page_layout.wrapper).removeClass('layout-wrapper-background');
  128. $(this.page_layout.main).removeClass('layout-main-section');
  129. $(this.page_layout.sidebar_area).toggle(false);
  130. } else {
  131. // module link
  132. this.setup_sidebar();
  133. }
  134. // footer
  135. this.setup_footer();
  136. // header - no headers for tables and guests
  137. if(!(this.meta.istable || user=='Guest' || this.meta.in_dialog))
  138. this.frm_head = new _f.FrmHeader(this.page_layout.head, this);
  139. // bg colour
  140. if(this.meta.colour)
  141. this.layout.wrapper.style.backgroundColor = '#'+this.meta.colour.split(':')[1];
  142. // create fields
  143. this.setup_fields_std();
  144. }
  145. _f.Frm.prototype.setup_print = function() {
  146. var l = []
  147. this.default_format = 'Standard';
  148. for(var key in locals['Print Format']) {
  149. if(locals['Print Format'][key].doc_type == this.meta.name) {
  150. l.push(locals['Print Format'][key].name);
  151. }
  152. }
  153. // if default print format is given, use it
  154. if(this.meta.default_print_format)
  155. this.default_format = this.meta.default_print_format;
  156. l.push('Standard');
  157. this.print_sel = $a(null, 'select', '', {width:'160px'});
  158. add_sel_options(this.print_sel, l);
  159. this.print_sel.value = this.default_format;
  160. }
  161. _f.Frm.prototype.print_doc = function() {
  162. if(this.doc.docstatus==2) {
  163. msgprint("Cannot Print Cancelled Documents.");
  164. return;
  165. }
  166. _p.show_dialog(); // multiple options
  167. }
  168. // ======================================================================================
  169. _f.Frm.prototype.email_doc = function() {
  170. // make selector
  171. if(!_e.dialog) _e.make();
  172. // set print selector
  173. sel = this.print_sel;
  174. var c = $td(_e.dialog.rows['Format'].tab,0,1);
  175. if(c.cur_sel) {
  176. c.removeChild(c.cur_sel);
  177. c.cur_sel = null;
  178. }
  179. c.appendChild(this.print_sel);
  180. c.cur_sel = this.print_sel;
  181. // hide / show attachments
  182. _e.dialog.widgets['Send With Attachments'].checked = 0;
  183. if(cur_frm.doc.file_list) {
  184. $ds(_e.dialog.rows['Send With Attachments']);
  185. } else {
  186. $dh(_e.dialog.rows['Send With Attachments']);
  187. }
  188. _e.dialog.widgets['Subject'].value = get_doctype_label(this.meta.name) + ': ' + this.docname;
  189. _e.dialog.show();
  190. }
  191. // ======================================================================================
  192. _f.Frm.prototype.rename_notify = function(dt, old, name) {
  193. // editable
  194. this.is_editable[name] = this.is_editable[old];
  195. delete this.is_editable[old];
  196. // from form
  197. if(this.docname == old)
  198. this.docname = name;
  199. // cleanup
  200. if(this && this.opendocs[old]) {
  201. // local doctype copy
  202. local_dt[dt][name] = local_dt[dt][old];
  203. local_dt[dt][old] = null;
  204. }
  205. delete this.opendocs[old];
  206. this.opendocs[name] = true;
  207. }
  208. // SETUP
  209. // ======================================================================================
  210. _f.Frm.prototype.setup_meta = function() {
  211. this.meta = get_local('DocType',this.doctype);
  212. this.perm = get_perm(this.doctype); // for create
  213. if(this.meta.istable) { this.meta.in_dialog = 1 }
  214. this.setup_print();
  215. }
  216. // --------------------------------------------------------------------------------------
  217. _f.Frm.prototype.setup_sidebar = function() {
  218. this.sidebar = new wn.widgets.form.sidebar.Sidebar(this);
  219. }
  220. // --------------------------------------------------------------------------------------
  221. _f.Frm.prototype.setup_footer = function() {
  222. var me = this;
  223. // footer toolbar
  224. var f = this.page_layout.footer;
  225. // save buttom
  226. f.save_area = $a(this.page_layout.footer,'div','',{display:'none', marginTop:'11px'});
  227. f.help_area = $a(this.page_layout.footer,'div');
  228. var b = $btn(f.save_area, 'Save',
  229. function() { cur_frm.save('Save'); },{marginLeft:'0px'},'green');
  230. // show / hide save
  231. f.show_save = function() {
  232. $ds(me.page_layout.footer.save_area);
  233. }
  234. f.hide_save = function() {
  235. $dh(me.page_layout.footer.save_area);
  236. }
  237. }
  238. // --------------------------------------------------------------------------------------
  239. _f.Frm.prototype.setup_fields_std = function() {
  240. var fl = fields_list[this.doctype];
  241. fl.sort(function(a,b) { return a.idx - b.idx});
  242. if(fl[0]&&fl[0].fieldtype!="Section Break" || get_url_arg('embed')) {
  243. this.layout.addrow(); // default section break
  244. if(fl[0].fieldtype!="Column Break") {// without column too
  245. var c = this.layout.addcell();
  246. $y(c.wrapper, {padding: '8px'});
  247. }
  248. }
  249. var sec;
  250. for(var i=0;i<fl.length;i++) {
  251. var f=fl[i];
  252. // if section break and next item
  253. // is a section break then ignore
  254. if(f.fieldtype=='Section Break' && fl[i+1] && fl[i+1].fieldtype=='Section Break')
  255. continue;
  256. var fn = f.fieldname?f.fieldname:f.label;
  257. var fld = make_field(f, this.doctype, this.layout.cur_cell, this);
  258. this.fields[this.fields.length] = fld;
  259. this.fields_dict[fn] = fld;
  260. if(sec && ['Section Break', 'Column Break'].indexOf(f.fieldtype)==-1) {
  261. fld.parent_section = sec;
  262. sec.fields.push(fld);
  263. }
  264. if(f.fieldtype=='Section Break') {
  265. sec = fld;
  266. this.sections.push(fld);
  267. }
  268. // default col-break after sec-break
  269. if((f.fieldtype=='Section Break')&&(fl[i+1])&&(fl[i+1].fieldtype!='Column Break')&&!f.hidden) {
  270. var c = this.layout.addcell();
  271. $y(c.wrapper, {padding: '8px'});
  272. }
  273. }
  274. }
  275. // --------------------------------------------------------------------------------------
  276. _f.Frm.prototype.add_custom_button = function(label, fn, icon) {
  277. this.frm_head.add_button(label, fn, icon);
  278. }
  279. _f.Frm.prototype.clear_custom_buttons = function() {
  280. //
  281. }
  282. // --------------------------------------------------------------------------------------
  283. _f.Frm.prototype.add_fetch = function(link_field, src_field, tar_field) {
  284. if(!this.fetch_dict[link_field]) {
  285. this.fetch_dict[link_field] = {'columns':[], 'fields':[]}
  286. }
  287. this.fetch_dict[link_field].columns.push(src_field);
  288. this.fetch_dict[link_field].fields.push(tar_field);
  289. }
  290. // --------------------------------------------------------------------------------------
  291. _f.Frm.prototype.setup_client_script = function() {
  292. // setup client obj
  293. if(this.meta.client_script_core || this.meta.client_script || this.meta.__js) {
  294. this.runclientscript('setup', this.doctype, this.docname);
  295. }
  296. }
  297. // --------------------------------------------------------------------------------------
  298. _f.Frm.prototype.refresh_print_layout = function() {
  299. $ds(this.print_wrapper);
  300. $dh(this.page_layout.wrapper);
  301. var me = this;
  302. var print_callback = function(print_html) {
  303. me.print_body.innerHTML = print_html;
  304. }
  305. // print head
  306. if(cur_frm.doc.select_print_heading)
  307. cur_frm.set_print_heading(cur_frm.doc.select_print_heading)
  308. if(user!='Guest') {
  309. $di(this.view_btn_wrapper);
  310. // archive
  311. if(cur_frm.doc.__archived) {
  312. $dh(this.view_btn_wrapper);
  313. }
  314. } else {
  315. $dh(this.view_btn_wrapper);
  316. $dh(this.print_close_btn);
  317. }
  318. // create print format here
  319. _p.build(this.default_format, print_callback, null, 1);
  320. }
  321. // --------------------------------------------------------------------------------------
  322. _f.Frm.prototype.show_the_frm = function() {
  323. // show the dialog
  324. if(this.meta.in_dialog && !this.parent.dialog.display) {
  325. if(!this.meta.istable)
  326. this.parent.table_form = false;
  327. this.parent.dialog.show();
  328. }
  329. }
  330. // --------------------------------------------------------------------------------------
  331. _f.Frm.prototype.set_print_heading = function(txt) {
  332. this.pformat[cur_frm.docname] = txt;
  333. }
  334. // --------------------------------------------------------------------------------------
  335. _f.Frm.prototype.defocus_rest = function() {
  336. // deselect others
  337. if(_f.cur_grid_cell) _f.cur_grid_cell.grid.cell_deselect();
  338. }
  339. // -------- Permissions -------
  340. // Returns global permissions, at all levels
  341. // ======================================================================================
  342. _f.Frm.prototype.get_doc_perms = function() {
  343. var p = [0,0,0,0,0,0];
  344. for(var i=0; i<this.perm.length; i++) {
  345. if(this.perm[i]) {
  346. if(this.perm[i][READ]) p[READ] = 1;
  347. if(this.perm[i][WRITE]) p[WRITE] = 1;
  348. if(this.perm[i][SUBMIT]) p[SUBMIT] = 1;
  349. if(this.perm[i][CANCEL]) p[CANCEL] = 1;
  350. if(this.perm[i][AMEND]) p[AMEND] = 1;
  351. }
  352. }
  353. return p;
  354. }
  355. // refresh
  356. // ======================================================================================
  357. _f.Frm.prototype.refresh_header = function() {
  358. // set title
  359. // main title
  360. if(!this.meta.in_dialog) {
  361. set_title(this.meta.issingle ? this.doctype : this.docname);
  362. }
  363. // form title
  364. this.page_layout.main_head.innerHTML = '<h2>'+this.docname+'</h2>';
  365. // show / hide buttons
  366. if(this.frm_head)this.frm_head.refresh();
  367. // add to recent
  368. if(wn.ui.toolbar.recent)
  369. wn.ui.toolbar.recent.add(this.doctype, this.docname, 1);
  370. }
  371. // --------------------------------------------------------------------------------------
  372. _f.Frm.prototype.check_doc_perm = function() {
  373. // get perm
  374. var dt = this.parent_doctype?this.parent_doctype : this.doctype;
  375. var dn = this.parent_docname?this.parent_docname : this.docname;
  376. this.perm = get_perm(dt, dn);
  377. this.orig_perm = get_perm(dt, dn, 1);
  378. if(!this.perm[0][READ]) {
  379. if(user=='Guest') {
  380. // allow temp access? via encryted akey
  381. if(_f.temp_access[dt] && _f.temp_access[dt][dn]) {
  382. this.perm = [[1,0,0]]
  383. return 1;
  384. }
  385. }
  386. window.back();
  387. return 0;
  388. }
  389. return 1
  390. }
  391. // --------------------------------------------------------------------------------------
  392. _f.Frm.prototype.refresh = function(docname) {
  393. // record switch
  394. if(docname) {
  395. if(this.docname != docname && !this.meta.in_dialog && !this.meta.istable) scroll(0, 0);
  396. this.docname = docname;
  397. }
  398. if(!this.meta.istable) {
  399. cur_frm = this;
  400. this.parent.cur_frm = this;
  401. }
  402. if(this.docname) { // document to show
  403. // check permissions
  404. if(!this.check_doc_perm()) return;
  405. // do setup
  406. if(!this.setup_done) this.setup();
  407. // set customized permissions for this record
  408. this.runclientscript('set_perm',this.doctype, this.docname);
  409. // set the doc
  410. this.doc = get_local(this.doctype, this.docname);
  411. // load the record for the first time, if not loaded (call 'onload')
  412. cur_frm.cscript.is_onload = false;
  413. if(!this.opendocs[this.docname]) {
  414. cur_frm.cscript.is_onload = true;
  415. this.setnewdoc(this.docname);
  416. }
  417. // editable
  418. if(this.doc.__islocal)
  419. this.is_editable[this.docname] = 1; // new is editable
  420. this.editable = this.is_editable[this.docname];
  421. if(!this.doc.__archived && (this.editable || (!this.editable && this.meta.istable))) {
  422. // show form layout (with fields etc)
  423. // ----------------------------------
  424. if(this.print_wrapper) {
  425. $dh(this.print_wrapper);
  426. $ds(this.page_layout.wrapper);
  427. }
  428. // header
  429. if(!this.meta.istable) {
  430. this.refresh_header();
  431. this.sidebar && this.sidebar.refresh();
  432. }
  433. // call trigger
  434. this.runclientscript('refresh');
  435. // trigger global trigger
  436. // to use this
  437. // $(docuemnt).bind('form_refresh', function() { })
  438. $(document).trigger('form_refresh');
  439. // fields
  440. this.refresh_fields();
  441. // dependent fields
  442. this.refresh_dependency();
  443. // footer
  444. this.refresh_footer();
  445. // layout
  446. if(this.layout) this.layout.show();
  447. // call onload post render for callbacks to be fired
  448. if(cur_frm.cscript.is_onload) {
  449. this.runclientscript('onload_post_render', this.doctype, this.docname);
  450. }
  451. // focus on first input
  452. if(this.doc.docstatus==0) {
  453. $(this.wrapper).find('.form-layout-row :input:first').focus();
  454. }
  455. } else {
  456. // show print layout
  457. // ----------------------------------
  458. this.refresh_header();
  459. if(this.print_wrapper) {
  460. this.refresh_print_layout();
  461. }
  462. this.runclientscript('edit_status_changed');
  463. }
  464. $(cur_frm.wrapper).trigger('render_complete');
  465. }
  466. }
  467. // --------------------------------------------------------------------------------------
  468. _f.Frm.prototype.refresh_footer = function() {
  469. var f = this.page_layout.footer;
  470. if(f.save_area) {
  471. if(get_url_arg('embed') || (this.editable && !this.meta.in_dialog && this.doc.docstatus==0 && !this.meta.istable && this.get_doc_perms()[WRITE])) {
  472. f.show_save();
  473. } else {
  474. f.hide_save();
  475. }
  476. }
  477. }
  478. // --------------------------------------------------------------------------------------
  479. _f.Frm.prototype.refresh_fields = function() {
  480. // refresh fields
  481. for(var i=0; i<this.fields.length; i++) {
  482. var f = this.fields[i];
  483. f.perm = this.perm;
  484. f.docname = this.docname;
  485. // if field is identifiable (not blank section or column break)
  486. // get the "customizable" parameters for this record
  487. var fn = f.df.fieldname || f.df.label;
  488. if(fn)
  489. f.df = get_field(this.doctype, fn, this.docname);
  490. if(f.df.fieldtype!='Section Break' && f.refresh) {
  491. f.refresh();
  492. }
  493. }
  494. // refresh sections
  495. $.each(this.sections, function(i, f) {
  496. f.refresh(true);
  497. })
  498. // cleanup activities after refresh
  499. this.cleanup_refresh(this);
  500. }
  501. // --------------------------------------------------------------------------------------
  502. _f.Frm.prototype.cleanup_refresh = function() {
  503. var me = this;
  504. if(me.fields_dict['amended_from']) {
  505. if (me.doc.amended_from) {
  506. unhide_field('amended_from'); unhide_field('amendment_date');
  507. } else {
  508. hide_field('amended_from'); hide_field('amendment_date');
  509. }
  510. }
  511. if(me.fields_dict['trash_reason']) {
  512. if(me.doc.trash_reason && me.doc.docstatus == 2) {
  513. unhide_field('trash_reason');
  514. } else {
  515. hide_field('trash_reason');
  516. }
  517. }
  518. if(me.meta.autoname && me.meta.autoname.substr(0,6)=='field:' && !me.doc.__islocal) {
  519. var fn = me.meta.autoname.substr(6);
  520. set_field_permlevel(fn,1); // make it readonly / hidden
  521. }
  522. }
  523. // Resolve "depends_on" and show / hide accordingly
  524. // ======================================================================================
  525. _f.Frm.prototype.refresh_dependency = function() {
  526. var me = this;
  527. var doc = locals[this.doctype][this.docname];
  528. // build dependants' dictionary
  529. var dep_dict = {};
  530. var has_dep = false;
  531. for(fkey in me.fields) {
  532. var f = me.fields[fkey];
  533. f.dependencies_clear = true;
  534. var guardian = f.df.depends_on;
  535. if(guardian) {
  536. if(!dep_dict[guardian])
  537. dep_dict[guardian] = [];
  538. dep_dict[guardian][dep_dict[guardian].length] = f;
  539. has_dep = true;
  540. }
  541. }
  542. if(!has_dep)return;
  543. // show / hide based on values
  544. for(var i=me.fields.length-1;i>=0;i--) {
  545. var f = me.fields[i];
  546. f.guardian_has_value = true;
  547. if(f.df.depends_on) {
  548. // evaluate guardian
  549. var v = doc[f.df.depends_on];
  550. if(f.df.depends_on.substr(0,5)=='eval:') {
  551. f.guardian_has_value = eval(f.df.depends_on.substr(5));
  552. } else if(f.df.depends_on.substr(0,3)=='fn:') {
  553. f.guardian_has_value = me.runclientscript(f.df.depends_on.substr(3), me.doctype, me.docname);
  554. } else {
  555. if(v || (v==0 && !v.substr)) {
  556. // guardian has value
  557. } else {
  558. f.guardian_has_value = false;
  559. }
  560. }
  561. // show / hide
  562. if(f.guardian_has_value) {
  563. if(f.grid)f.grid.show(); else $ds(f.wrapper);
  564. } else {
  565. if(f.grid)f.grid.hide(); else $dh(f.wrapper);
  566. }
  567. }
  568. }
  569. }
  570. // setnewdoc is called when a record is loaded for the first time
  571. // ======================================================================================
  572. _f.Frm.prototype.setnewdoc = function(docname) {
  573. this.check_doctype_conflict(docname);
  574. // if loaded
  575. if(this.opendocs[docname]) { // already exists
  576. this.docname=docname;
  577. return;
  578. }
  579. //if(!this.meta)
  580. // this.setup_meta();
  581. // make a copy of the doctype for client script settings
  582. // each record will have its own client script
  583. Meta.make_local_dt(this.doctype,docname);
  584. this.docname = docname;
  585. var me = this;
  586. var viewname = docname;
  587. if(this.meta.issingle) viewname = this.doctype;
  588. // Client Script
  589. this.runclientscript('onload', this.doctype, this.docname);
  590. this.is_editable[docname] = 1;
  591. if(this.meta.read_only_onload) this.is_editable[docname] = 0;
  592. this.opendocs[docname] = true;
  593. }
  594. _f.Frm.prototype.edit_doc = function() {
  595. // set fields
  596. this.is_editable[this.docname] = true;
  597. this.refresh();
  598. }
  599. _f.Frm.prototype.show_doc = function(dn) {
  600. this.refresh(dn);
  601. }
  602. // ======================================================================================
  603. var validated; // bad design :(
  604. _f.Frm.prototype.save = function(save_action, call_back) {
  605. //alert(save_action);
  606. if(!save_action) save_action = 'Save';
  607. var me = this;
  608. if(this.savingflag) {
  609. msgprint("Document is currently saving....");
  610. return; // already saving (do not double save)
  611. }
  612. if(save_action=='Submit') {
  613. locals[this.doctype][this.docname].submitted_on = dateutil.full_str();
  614. locals[this.doctype][this.docname].submitted_by = user;
  615. }
  616. if(save_action=='Trash') {
  617. var reason = prompt('Reason for trash (mandatory)', '');
  618. if(!strip(reason)) {
  619. msgprint('Reason is mandatory, not trashed');
  620. return;
  621. }
  622. locals[this.doctype][this.docname].trash_reason = reason;
  623. }
  624. // run validations
  625. if(save_action=='Cancel') {
  626. var reason = prompt('Reason for cancellation (mandatory)', '');
  627. if(!strip(reason)) {
  628. msgprint('Reason is mandatory, not cancelled');
  629. return;
  630. }
  631. locals[this.doctype][this.docname].cancel_reason = reason;
  632. locals[this.doctype][this.docname].cancelled_on = dateutil.full_str();
  633. locals[this.doctype][this.docname].cancelled_by = user;
  634. } else if(save_action=='Update') {
  635. // no validation for update
  636. } else { // no validation for cancellation
  637. validated = true;
  638. if(this.cscript.validate)
  639. this.runclientscript('validate', this.doctype, this.docname);
  640. if(!validated) {
  641. this.savingflag = false;
  642. return 'Error';
  643. }
  644. }
  645. var ret_fn = function(r) {
  646. me.savingflag = false;
  647. if(user=='Guest' && !r.exc) {
  648. // if user is guest, show a message after succesful saving
  649. $dh(me.page_layout.wrapper);
  650. $ds(me.saved_wrapper);
  651. me.saved_wrapper.innerHTML =
  652. '<div style="padding: 150px 16px; text-align: center; font-size: 14px;">'
  653. + (cur_frm.message_after_save ? cur_frm.message_after_save : 'Your information has been sent. Thank you!')
  654. + '</div>';
  655. return; // no refresh
  656. }
  657. if(!me.meta.istable) {
  658. me.refresh();
  659. }
  660. if(call_back){
  661. call_back(r);
  662. }
  663. }
  664. var me = this;
  665. var ret_fn_err = function(r) {
  666. var doc = locals[me.doctype][me.docname];
  667. me.savingflag = false;
  668. ret_fn(r);
  669. }
  670. this.savingflag = true;
  671. if(this.docname && validated) {
  672. // scroll to top
  673. scroll(0, 0);
  674. return this.savedoc(save_action, ret_fn, ret_fn_err);
  675. }
  676. }
  677. // ======================================================================================
  678. _f.Frm.prototype.runscript = function(scriptname, callingfield, onrefresh) {
  679. var me = this;
  680. if(this.docname) {
  681. // make doc list
  682. var doclist = compress_doclist(make_doclist(this.doctype, this.docname));
  683. // send to run
  684. if(callingfield)callingfield.input.disabled = true;
  685. $c('runserverobj', {'docs':doclist, 'method':scriptname },
  686. function(r, rtxt) {
  687. // run refresh
  688. if(onrefresh)
  689. onrefresh(r,rtxt);
  690. // fields
  691. me.refresh_fields();
  692. // dependent fields
  693. me.refresh_dependency();
  694. // enable button
  695. if(callingfield)callingfield.input.done_working();
  696. }
  697. );
  698. }
  699. }
  700. // ======================================================================================
  701. _f.Frm.prototype.runclientscript = function(caller, cdt, cdn) {
  702. var _dt = this.parent_doctype ? this.parent_doctype : this.doctype;
  703. var _dn = this.parent_docname ? this.parent_docname : this.docname;
  704. var doc = get_local(_dt, _dn);
  705. if(!cdt)cdt = this.doctype;
  706. if(!cdn)cdn = this.docname;
  707. var ret = null;
  708. try {
  709. if(this.cscript[caller])
  710. ret = this.cscript[caller](doc, cdt, cdn);
  711. // for product
  712. if(this.cscript['custom_'+caller])
  713. ret += this.cscript['custom_'+caller](doc, cdt, cdn);
  714. } catch(e) {
  715. console.log(e);
  716. }
  717. if(caller && caller.toLowerCase()=='setup') {
  718. var doctype = get_local('DocType', this.doctype);
  719. // js
  720. var cs = doctype.__js || (doctype.client_script_core + doctype.client_script);
  721. if(cs) {
  722. try {
  723. var tmp = eval(cs);
  724. } catch(e) {
  725. console.log(e);
  726. }
  727. }
  728. // css
  729. if(doctype.__css) set_style(doctype.__css)
  730. // ---Client String----
  731. if(doctype.client_string) { // split client string
  732. this.cstring = {};
  733. var elist = doctype.client_string.split('---');
  734. for(var i=1;i<elist.length;i=i+2) {
  735. this.cstring[strip(elist[i])] = elist[i+1];
  736. }
  737. }
  738. }
  739. return ret;
  740. }
  741. // ======================================================================================
  742. _f.Frm.prototype.copy_doc = function(onload, from_amend) {
  743. if(!this.perm[0][CREATE]) {
  744. msgprint('You are not allowed to create '+this.meta.name);
  745. return;
  746. }
  747. var dn = this.docname;
  748. // copy parent
  749. var newdoc = LocalDB.copy(this.doctype, dn, from_amend);
  750. // do not copy attachments
  751. if(this.meta.allow_attach && newdoc.file_list)
  752. newdoc.file_list = null;
  753. // copy chidren
  754. var dl = make_doclist(this.doctype, dn);
  755. // table fields dict - for no_copy check
  756. var tf_dict = {};
  757. for(var d in dl) {
  758. d1 = dl[d];
  759. // get tabel field
  760. if(!tf_dict[d1.parentfield]) {
  761. tf_dict[d1.parentfield] = get_field(d1.parenttype, d1.parentfield);
  762. }
  763. if(d1.parent==dn && cint(tf_dict[d1.parentfield].no_copy)!=1) {
  764. var ch = LocalDB.copy(d1.doctype, d1.name, from_amend);
  765. ch.parent = newdoc.name;
  766. ch.docstatus = 0;
  767. ch.owner = user;
  768. ch.creation = '';
  769. ch.modified_by = user;
  770. ch.modified = '';
  771. }
  772. }
  773. newdoc.__islocal = 1;
  774. newdoc.docstatus = 0;
  775. newdoc.owner = user;
  776. newdoc.creation = '';
  777. newdoc.modified_by = user;
  778. newdoc.modified = '';
  779. if(onload)onload(newdoc);
  780. loaddoc(newdoc.doctype, newdoc.name);
  781. }
  782. // ======================================================================================
  783. _f.Frm.prototype.reload_doc = function() {
  784. this.check_doctype_conflict(this.docname);
  785. var me = this;
  786. var ret_fn = function(r, rtxt) {
  787. // n tweets and last comment
  788. me.runclientscript('setup', me.doctype, me.docname);
  789. me.refresh();
  790. }
  791. if(me.doc.__islocal) {
  792. // reload only doctype
  793. $c('webnotes.widgets.form.load.getdoctype', {'doctype':me.doctype }, ret_fn, null, null, 'Refreshing ' + me.doctype + '...');
  794. } else {
  795. // delete all unsaved rows
  796. var gl = me.grids;
  797. for(var i = 0; i < gl.length; i++) {
  798. var dt = gl[i].df.options;
  799. for(var dn in locals[dt]) {
  800. if(locals[dt][dn].__islocal && locals[dt][dn].parent == me.docname) {
  801. var d = locals[dt][dn];
  802. d.parent = '';
  803. d.docstatus = 2;
  804. d.__deleted = 1;
  805. }
  806. }
  807. }
  808. // reload doc and docytpe
  809. $c('webnotes.widgets.form.load.getdoc', {'name':me.docname, 'doctype':me.doctype, 'getdoctype':1, 'user':user}, ret_fn, null, null, 'Refreshing ' + me.docname + '...');
  810. }
  811. }
  812. // ======================================================================================
  813. _f.Frm.prototype.savedoc = function(save_action, onsave, onerr) {
  814. this.error_in_section = 0;
  815. save_doclist(this.doctype, this.docname, save_action, onsave, onerr);
  816. }
  817. _f.Frm.prototype.saveupdate = function() {
  818. this.save('Update');
  819. }
  820. _f.Frm.prototype.savesubmit = function() {
  821. var answer = confirm("Permanently Submit "+this.docname+"?");
  822. var me = this;
  823. if(answer) {
  824. this.save('Submit', function(r) {
  825. if(!r.exc && me.cscript.on_submit) {
  826. me.runclientscript('on_submit', me.doctype, me.docname);
  827. }
  828. });
  829. }
  830. }
  831. _f.Frm.prototype.savecancel = function() {
  832. var answer = confirm("Permanently Cancel "+this.docname+"?");
  833. if(answer) this.save('Cancel');
  834. }
  835. // delete the record
  836. _f.Frm.prototype.savetrash = function() {
  837. var me = this;
  838. var answer = confirm("Permanently Delete "+this.docname+"? This action cannot be reversed");
  839. if(answer) {
  840. $c('webnotes.model.delete_doc', {dt:this.doctype, dn:this.docname}, function(r,rt) {
  841. if(r.message=='okay') {
  842. // delete from locals
  843. LocalDB.delete_doc(me.doctype, me.docname);
  844. // delete from recent
  845. if(wn.ui.toolbar.recent) wn.ui.toolbar.recent.remove(me.doctype, me.docname);
  846. // "close"
  847. window.back();
  848. }
  849. })
  850. }
  851. }
  852. // ======================================================================================
  853. _f.Frm.prototype.amend_doc = function() {
  854. if(!this.fields_dict['amended_from']) {
  855. alert('"amended_from" field must be present to do an amendment.');
  856. return;
  857. }
  858. var me = this;
  859. var fn = function(newdoc) {
  860. newdoc.amended_from = me.docname;
  861. if(me.fields_dict && me.fields_dict['amendment_date'])
  862. newdoc.amendment_date = dateutil.obj_to_str(new Date());
  863. }
  864. this.copy_doc(fn, 1);
  865. }
  866. // ======================================================================================
  867. _f.get_value = function(dt, dn, fn) {
  868. if(locals[dt] && locals[dt][dn])
  869. return locals[dt][dn][fn];
  870. }
  871. _f.set_value = function(dt, dn, fn, v) {
  872. var d = locals[dt][dn];
  873. if(!d) {
  874. console.log('_f.set_value - '+ fn+': "'+dt+','+dn+'" not found');
  875. return;
  876. }
  877. var changed = d[fn] != v;
  878. if(changed && (d[fn]==null || v==null) && (cstr(d[fn])==cstr(v))) changed = 0;
  879. if(changed) {
  880. d[fn] = v;
  881. d.__unsaved = 1;
  882. var frm = wn.views.formview[d.doctype].frm;
  883. try {
  884. if(d.parent && d.parenttype) {
  885. locals[d.parenttype][d.parent].__unsaved = 1;
  886. frm = wn.views.formview[d.parenttype].frm;
  887. }
  888. } catch(e) {
  889. if(d.parent && d.parenttype)
  890. errprint('Setting __unsaved error:'+d.name+','+d.parent+','+d.parenttype);
  891. }
  892. if(frm && frm==cur_frm && frm.frm_head) {
  893. frm.frm_head.refresh_labels();
  894. }
  895. }
  896. }
  897. // ======================================================================================
  898. _f.Frm.prototype.show_comments = function() {
  899. if(!cur_frm.comments) {
  900. cur_frm.comments = new Dialog(540, 400, 'Comments');
  901. cur_frm.comments.comment_body = $a(cur_frm.comments.body, 'div', 'dialog_frm');
  902. $y(cur_frm.comments.body, {backgroundColor:'#EEE'});
  903. cur_frm.comments.list = new CommentList(cur_frm.comments.comment_body);
  904. }
  905. cur_frm.comments.list.dt = cur_frm.doctype;
  906. cur_frm.comments.list.dn = cur_frm.docname;
  907. cur_frm.comments.show();
  908. cur_frm.comments.list.run();
  909. }