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