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 26 KiB

13 years ago
13 years ago
13 years ago
12 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
12 years ago
13 years ago
13 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
13 years ago
12 years ago
12 years ago
12 years ago
12 years ago
13 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068
  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. + wn.PageLayout (this.page_layout)
  27. + this.wrapper
  28. + this.wtab (table)
  29. + this.main
  30. + this.head
  31. + this.body
  32. + this.layout
  33. + this.footer
  34. + this.sidebar
  35. + this.print_wrapper
  36. + this.head
  37. */
  38. wn.provide('_f');
  39. wn.provide('wn.ui.form');
  40. wn.ui.form.Controller = Class.extend({
  41. init: function(opts) {
  42. $.extend(this, opts);
  43. this.setup && this.setup();
  44. }
  45. });
  46. _f.frms = {};
  47. _f.Frm = function(doctype, parent, in_form) {
  48. this.docname = '';
  49. this.doctype = doctype;
  50. this.display = 0;
  51. this.refresh_if_stale_for = 120;
  52. var me = this;
  53. this.last_view_is_edit = {};
  54. this.opendocs = {};
  55. this.sections = [];
  56. this.grids = [];
  57. this.cscript = new wn.ui.form.Controller({frm:this});
  58. this.pformat = {};
  59. this.fetch_dict = {};
  60. this.parent = parent;
  61. this.tinymce_id_list = [];
  62. this.setup_meta(doctype);
  63. // show in form instead of in dialog, when called using url (router.js)
  64. this.in_form = in_form ? true : false;
  65. // notify on rename
  66. var me = this;
  67. $(document).bind('rename', function(event, dt, old_name, new_name) {
  68. if(dt==me.doctype)
  69. me.rename_notify(dt, old_name, new_name)
  70. });
  71. }
  72. _f.Frm.prototype.check_doctype_conflict = function(docname) {
  73. var me = this;
  74. if(this.doctype=='DocType' && docname=='DocType') {
  75. msgprint('Allowing DocType, DocType. Be careful!')
  76. } else if(this.doctype=='DocType') {
  77. if (wn.views.formview[docname] || wn.pages['List/'+docname]) {
  78. msgprint("Cannot open DocType when its instance is open")
  79. throw 'doctype open conflict'
  80. }
  81. } else {
  82. if (wn.views.formview.DocType && wn.views.formview.DocType.frm.opendocs[this.doctype]) {
  83. msgprint("Cannot open instance when its DocType is open")
  84. throw 'doctype open conflict'
  85. }
  86. }
  87. }
  88. _f.Frm.prototype.setup = function() {
  89. var me = this;
  90. this.fields = [];
  91. this.fields_dict = {};
  92. this.state_fieldname = wn.workflow.get_state_fieldname(this.doctype);
  93. // wrapper
  94. this.wrapper = this.parent;
  95. // create area for print fomrat
  96. this.setup_print_layout();
  97. // 2 column layout
  98. this.setup_std_layout();
  99. // client script must be called after "setup" - there are no fields_dict attached to the frm otherwise
  100. this.setup_client_script();
  101. this.setup_header();
  102. this.setup_done = true;
  103. }
  104. _f.Frm.prototype.setup_print_layout = function() {
  105. var me = this;
  106. this.print_wrapper = $a(this.wrapper, 'div');
  107. wn.ui.make_app_page({
  108. parent: this.print_wrapper,
  109. single_column: true,
  110. set_document_title: false,
  111. title: me.doctype + ": Print View",
  112. module: me.meta.module
  113. });
  114. var appframe = this.print_wrapper.appframe;
  115. appframe.add_button(wn._("View Details"), function() {
  116. me.edit_doc();
  117. }).addClass("btn-success");
  118. appframe.add_button(wn._("Print"), function() {
  119. me.print_doc();
  120. }, 'icon-print');
  121. this.$print_view_select = appframe.add_select(wn._("Select Preview"), this.print_formats)
  122. .css({"float":"right"})
  123. .val(this.print_formats[0])
  124. .change(function() {
  125. me.refresh_print_layout();
  126. })
  127. appframe.add_ripped_paper_effect(this.print_wrapper);
  128. var layout_main = $(this.print_wrapper).find(".layout-main");
  129. this.print_body = $("<div style='margin: 25px'>").appendTo(layout_main)
  130. .css("min-height", "400px").get(0);
  131. }
  132. _f.Frm.prototype.onhide = function() {
  133. if(_f.cur_grid_cell) _f.cur_grid_cell.grid.cell_deselect();
  134. }
  135. _f.Frm.prototype.setup_std_layout = function() {
  136. this.page_layout = new wn.PageLayout({
  137. parent: this.wrapper,
  138. main_width: (this.meta.in_dialog && !this.in_form) ? '100%' : '75%',
  139. sidebar_width: (this.meta.in_dialog && !this.in_form) ? '0%' : '25%'
  140. })
  141. // only tray
  142. this.meta.section_style='Simple'; // always simple!
  143. // layout
  144. this.layout = new Layout(this.page_layout.body, '100%');
  145. // sidebar
  146. if(this.meta.in_dialog && !this.in_form) {
  147. // hide sidebar
  148. $(this.page_layout.wrapper).removeClass('layout-wrapper-background');
  149. $(this.page_layout.main).removeClass('layout-main-section');
  150. $(this.page_layout.sidebar_area).toggle(false);
  151. } else {
  152. // module link
  153. this.setup_sidebar();
  154. }
  155. // state
  156. this.states = new wn.ui.form.States({
  157. frm: this
  158. });
  159. // watermark
  160. if(!this.meta.issingle) {
  161. $('<div style="font-size: 21px; color: #aaa; float: right;\
  162. margin-top: -5px; margin-right: -5px; z-index: 5;">'
  163. + wn._(this.doctype) + '</div>')
  164. .prependTo(this.page_layout.main);
  165. }
  166. // footer
  167. this.setup_footer();
  168. // create fields
  169. this.setup_fields_std();
  170. }
  171. _f.Frm.prototype.setup_header = function() {
  172. // header - no headers for tables and guests
  173. if(!(this.meta.istable || (this.meta.in_dialog && !this.in_form)))
  174. this.frm_head = new _f.FrmHeader(this.page_layout.head, this);
  175. }
  176. _f.Frm.prototype.setup_print = function() {
  177. this.print_formats = wn.meta.get_print_formats(this.meta.name);
  178. this.print_sel = $a(null, 'select', '', {width:'160px'});
  179. add_sel_options(this.print_sel, this.print_formats);
  180. this.print_sel.value = this.print_formats[0];
  181. }
  182. _f.Frm.prototype.print_doc = function() {
  183. if(this.doc.docstatus==2) {
  184. msgprint("Cannot Print Cancelled Documents.");
  185. return;
  186. }
  187. _p.show_dialog(); // multiple options
  188. }
  189. // email the form
  190. _f.Frm.prototype.email_doc = function(message) {
  191. new wn.views.CommunicationComposer({
  192. doc: this.doc,
  193. subject: wn._(this.meta.name) + ': ' + this.docname,
  194. recipients: this.doc.email || this.doc.email_id || this.doc.contact_email,
  195. attach_document_print: true,
  196. message: message,
  197. real_name: this.doc.real_name || this.doc.contact_display || this.doc.contact_name
  198. });
  199. }
  200. // email the form
  201. _f.Frm.prototype.rename_doc = function() {
  202. wn.model.rename_doc(this.doctype, this.docname);
  203. }
  204. // notify this form of renamed records
  205. _f.Frm.prototype.rename_notify = function(dt, old, name) {
  206. // from form
  207. if(this.meta.istable)
  208. return;
  209. if(this.docname == old)
  210. this.docname = name;
  211. else
  212. return;
  213. // view_is_edit
  214. this.last_view_is_edit[name] = this.last_view_is_edit[old];
  215. delete this.last_view_is_edit[old];
  216. // cleanup
  217. if(this && this.opendocs[old]) {
  218. // delete docfield copy
  219. wn.meta.docfield_copy[dt][name] = wn.meta.docfield_copy[dt][old];
  220. delete wn.meta.docfield_copy[dt][old];
  221. }
  222. delete this.opendocs[old];
  223. this.opendocs[name] = true;
  224. if(this.meta.in_dialog || !this.in_form) {
  225. return;
  226. }
  227. wn.re_route[window.location.hash] = '#Form/' + encodeURIComponent(this.doctype) + '/' + encodeURIComponent(name);
  228. wn.set_route('Form', this.doctype, name);
  229. }
  230. // SETUP
  231. _f.Frm.prototype.setup_meta = function(doctype) {
  232. this.meta = wn.model.get_doc('DocType',this.doctype);
  233. this.perm = wn.perm.get_perm(this.doctype); // for create
  234. if(this.meta.istable) { this.meta.in_dialog = 1 }
  235. this.setup_print();
  236. }
  237. _f.Frm.prototype.setup_sidebar = function() {
  238. this.sidebar = new wn.widgets.form.sidebar.Sidebar(this);
  239. }
  240. _f.Frm.prototype.setup_footer = function() {
  241. var me = this;
  242. // footer toolbar
  243. var f = this.page_layout.footer;
  244. // save buttom
  245. f.save_area = $a(this.page_layout.footer,'div','',{display:'none', marginTop:'11px'});
  246. f.help_area = $a(this.page_layout.footer,'div');
  247. var b = $("<button class='btn btn-info'><i class='icon-save'></i> Save</button>")
  248. .click(function() { me.save("Save", null, me); }).appendTo(f.save_area);
  249. // show / hide save
  250. f.show_save = function() {
  251. $ds(me.page_layout.footer.save_area);
  252. }
  253. f.hide_save = function() {
  254. $dh(me.page_layout.footer.save_area);
  255. }
  256. }
  257. _f.Frm.prototype.set_intro = function(txt) {
  258. wn.utils.set_intro(this, this.page_layout.body, txt);
  259. }
  260. _f.Frm.prototype.set_footnote = function(txt) {
  261. wn.utils.set_footnote(this, this.page_layout.body, txt);
  262. }
  263. _f.Frm.prototype.setup_fields_std = function() {
  264. var fl = wn.meta.docfield_list[this.doctype];
  265. fl.sort(function(a,b) { return a.idx - b.idx});
  266. if(fl[0]&&fl[0].fieldtype!="Section Break" || get_url_arg('embed')) {
  267. this.layout.addrow(); // default section break
  268. if(fl[0].fieldtype!="Column Break") {// without column too
  269. var c = this.layout.addcell();
  270. $y(c.wrapper, {padding: '8px'});
  271. }
  272. }
  273. var sec;
  274. for(var i=0;i<fl.length;i++) {
  275. var f=fl[i];
  276. // if section break and next item
  277. // is a section break then ignore
  278. if(f.fieldtype=='Section Break' && fl[i+1] && fl[i+1].fieldtype=='Section Break')
  279. continue;
  280. var fn = f.fieldname?f.fieldname:f.label;
  281. var fld = make_field(f, this.doctype, this.layout.cur_cell, this);
  282. this.fields[this.fields.length] = fld;
  283. this.fields_dict[fn] = fld;
  284. if(sec && ['Section Break', 'Column Break'].indexOf(f.fieldtype)==-1) {
  285. fld.parent_section = sec;
  286. sec.fields.push(fld);
  287. }
  288. if(f.fieldtype=='Section Break') {
  289. sec = fld;
  290. this.sections.push(fld);
  291. }
  292. // default col-break after sec-break
  293. if((f.fieldtype=='Section Break')&&(fl[i+1])&&(fl[i+1].fieldtype!='Column Break')) {
  294. var c = this.layout.addcell();
  295. $y(c.wrapper, {padding: '8px'});
  296. }
  297. }
  298. }
  299. _f.Frm.prototype.add_custom_button = function(label, fn, icon) {
  300. this.frm_head.appframe.add_button(label, fn, icon);
  301. }
  302. _f.Frm.prototype.clear_custom_buttons = function() {
  303. this.frm_head.refresh_toolbar()
  304. }
  305. _f.Frm.prototype.add_fetch = function(link_field, src_field, tar_field) {
  306. if(!this.fetch_dict[link_field]) {
  307. this.fetch_dict[link_field] = {'columns':[], 'fields':[]}
  308. }
  309. this.fetch_dict[link_field].columns.push(src_field);
  310. this.fetch_dict[link_field].fields.push(tar_field);
  311. }
  312. _f.Frm.prototype.setup_client_script = function() {
  313. // setup client obj
  314. if(this.meta.client_script_core || this.meta.client_script || this.meta.__js) {
  315. this.runclientscript('setup', this.doctype, this.docname);
  316. }
  317. }
  318. _f.Frm.prototype.refresh_print_layout = function() {
  319. $ds(this.print_wrapper);
  320. $dh(this.page_layout.wrapper);
  321. var me = this;
  322. var print_callback = function(print_html) {
  323. me.print_body.innerHTML = print_html;
  324. }
  325. // print head
  326. if(cur_frm.doc.select_print_heading)
  327. cur_frm.set_print_heading(cur_frm.doc.select_print_heading)
  328. if(user!='Guest') {
  329. $di(this.view_btn_wrapper);
  330. // archive
  331. if(cur_frm.doc.__archived) {
  332. $dh(this.view_btn_wrapper);
  333. }
  334. } else {
  335. $dh(this.view_btn_wrapper);
  336. $dh(this.print_close_btn);
  337. }
  338. // create print format here
  339. _p.build(this.$print_view_select.val(), print_callback, null, 1);
  340. }
  341. _f.Frm.prototype.show_the_frm = function() {
  342. // show the dialog
  343. if(this.meta.in_dialog && !this.parent.dialog.display) {
  344. if(!this.meta.istable)
  345. this.parent.table_form = false;
  346. this.parent.dialog.show();
  347. }
  348. }
  349. _f.Frm.prototype.set_print_heading = function(txt) {
  350. this.pformat[cur_frm.docname] = txt;
  351. }
  352. _f.Frm.prototype.defocus_rest = function() {
  353. // deselect others
  354. if(_f.cur_grid_cell) _f.cur_grid_cell.grid.cell_deselect();
  355. }
  356. _f.Frm.prototype.refresh_header = function() {
  357. // set title
  358. // main title
  359. if(!this.meta.in_dialog || this.in_form) {
  360. set_title(this.meta.issingle ? this.doctype : this.docname);
  361. }
  362. if(wn.ui.toolbar.recent)
  363. wn.ui.toolbar.recent.add(this.doctype, this.docname, 1);
  364. // show / hide buttons
  365. if(this.frm_head)this.frm_head.refresh();
  366. }
  367. _f.Frm.prototype.check_doc_perm = function() {
  368. // get perm
  369. var dt = this.parent_doctype?this.parent_doctype : this.doctype;
  370. var dn = this.parent_docname?this.parent_docname : this.docname;
  371. this.perm = wn.perm.get_perm(dt, dn);
  372. if(!this.perm[0][READ]) {
  373. wn.set_route("403");
  374. return 0;
  375. }
  376. return 1
  377. }
  378. _f.Frm.prototype.refresh = function(docname) {
  379. // record switch
  380. if(docname) {
  381. if(this.docname != docname && (!this.meta.in_dialog || this.in_form) &&
  382. !this.meta.istable)
  383. scroll(0, 0);
  384. this.docname = docname;
  385. }
  386. if(!this.meta.istable) {
  387. cur_frm = this;
  388. this.parent.cur_frm = this;
  389. }
  390. if(this.docname) { // document to show
  391. // check permissions
  392. if(!this.check_doc_perm()) return;
  393. // read only (workflow)
  394. this.read_only = wn.workflow.is_read_only(this.doctype, this.docname);
  395. // set the doc
  396. this.doc = wn.model.get_doc(this.doctype, this.docname);
  397. // check if doctype is already open
  398. if (!this.opendocs[this.docname]) {
  399. this.check_doctype_conflict(this.docname);
  400. } else {
  401. if(this.doc && this.doc.__last_sync_on &&
  402. (new Date() - this.doc.__last_sync_on) > (this.refresh_if_stale_for * 1000)) {
  403. this.reload_doc();
  404. return;
  405. }
  406. }
  407. // do setup
  408. if(!this.setup_done) this.setup();
  409. // set customized permissions for this record
  410. this.runclientscript('set_perm',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. // view_is_edit
  418. if(this.doc.__islocal)
  419. this.last_view_is_edit[this.docname] = 1; // new is view_is_edit
  420. this.view_is_edit = this.last_view_is_edit[this.docname];
  421. if(this.view_is_edit || (!this.view_is_edit && this.meta.istable)) {
  422. if(this.print_wrapper) {
  423. $dh(this.print_wrapper);
  424. $ds(this.page_layout.wrapper);
  425. }
  426. // header
  427. if(!this.meta.istable) {
  428. this.refresh_header();
  429. this.sidebar && this.sidebar.refresh();
  430. }
  431. // call trigger
  432. this.runclientscript('refresh');
  433. // trigger global trigger
  434. // to use this
  435. $(document).trigger('form_refresh');
  436. // fields
  437. this.refresh_fields();
  438. // dependent fields
  439. this.refresh_dependency();
  440. // footer
  441. this.refresh_footer();
  442. // layout
  443. if(this.layout) this.layout.show();
  444. // call onload post render for callbacks to be fired
  445. if(this.cscript.is_onload) {
  446. this.runclientscript('onload_post_render', this.doctype, this.docname);
  447. }
  448. // focus on first input
  449. if(this.doc.docstatus==0) {
  450. var first = $(this.wrapper).find('.form-layout-row :input:first');
  451. if(!in_list(["Date", "Datetime"], first.attr("data-fieldtype"))) {
  452. first.focus();
  453. }
  454. }
  455. } else {
  456. this.refresh_header();
  457. if(this.print_wrapper) {
  458. this.refresh_print_layout();
  459. }
  460. this.runclientscript('edit_status_changed');
  461. }
  462. $(cur_frm.wrapper).trigger('render_complete');
  463. }
  464. }
  465. _f.Frm.prototype.refresh_footer = function() {
  466. var f = this.page_layout.footer;
  467. if(f.save_area) {
  468. // if save button is there in the header
  469. if(this.frm_head && this.frm_head.appframe.toolbar
  470. && this.frm_head.appframe.toolbar.find(".btn-save").length && !this.save_disabled
  471. && (this.fields && this.fields.length > 7)) {
  472. f.show_save();
  473. } else {
  474. f.hide_save();
  475. }
  476. }
  477. }
  478. _f.Frm.prototype.refresh_field = function(fname) {
  479. cur_frm.fields_dict[fname] && cur_frm.fields_dict[fname].refresh
  480. && cur_frm.fields_dict[fname].refresh();
  481. }
  482. _f.Frm.prototype.refresh_fields = function() {
  483. // refresh fields
  484. for(var i=0; i<this.fields.length; i++) {
  485. var f = this.fields[i];
  486. f.perm = this.perm;
  487. f.docname = this.docname;
  488. // if field is identifiable (not blank section or column break)
  489. // get the "customizable" parameters for this record
  490. var fn = f.df.fieldname || f.df.label;
  491. if(fn)
  492. f.df = wn.meta.get_docfield(this.doctype, fn, this.docname);
  493. if(f.df.fieldtype!='Section Break' && f.refresh) {
  494. f.refresh();
  495. }
  496. }
  497. // refresh sections
  498. $.each(this.sections, function(i, f) {
  499. f.refresh(true);
  500. })
  501. // cleanup activities after refresh
  502. this.cleanup_refresh(this);
  503. }
  504. _f.Frm.prototype.cleanup_refresh = function() {
  505. var me = this;
  506. if(me.fields_dict['amended_from']) {
  507. if (me.doc.amended_from) {
  508. unhide_field('amended_from');
  509. if (me.fields_dict['amendment_date']) unhide_field('amendment_date');
  510. } else {
  511. hide_field('amended_from');
  512. if (me.fields_dict['amendment_date']) hide_field('amendment_date');
  513. }
  514. }
  515. if(me.fields_dict['trash_reason']) {
  516. if(me.doc.trash_reason && me.doc.docstatus == 2) {
  517. unhide_field('trash_reason');
  518. } else {
  519. hide_field('trash_reason');
  520. }
  521. }
  522. if(me.meta.autoname && me.meta.autoname.substr(0,6)=='field:' && !me.doc.__islocal) {
  523. var fn = me.meta.autoname.substr(6);
  524. cur_frm.toggle_display(fn, false);
  525. }
  526. if(me.meta.autoname=="naming_series:" && !me.doc.__islocal) {
  527. cur_frm.toggle_display("naming_series", false);
  528. }
  529. }
  530. // Resolve "depends_on" and show / hide accordingly
  531. _f.Frm.prototype.refresh_dependency = function() {
  532. var me = this;
  533. var doc = locals[this.doctype][this.docname];
  534. // build dependants' dictionary
  535. var has_dep = false;
  536. for(fkey in me.fields) {
  537. var f = me.fields[fkey];
  538. f.dependencies_clear = true;
  539. if(f.df.depends_on) {
  540. has_dep = true;
  541. }
  542. }
  543. if(!has_dep)return;
  544. // show / hide based on values
  545. for(var i=me.fields.length-1;i>=0;i--) {
  546. var f = me.fields[i];
  547. f.guardian_has_value = true;
  548. if(f.df.depends_on) {
  549. // evaluate guardian
  550. var v = doc[f.df.depends_on];
  551. if(f.df.depends_on.substr(0,5)=='eval:') {
  552. f.guardian_has_value = eval(f.df.depends_on.substr(5));
  553. } else if(f.df.depends_on.substr(0,3)=='fn:') {
  554. f.guardian_has_value = me.runclientscript(f.df.depends_on.substr(3), me.doctype, me.docname);
  555. } else {
  556. if(!v) {
  557. f.guardian_has_value = false;
  558. }
  559. }
  560. // show / hide
  561. if(f.guardian_has_value) {
  562. f.df.hidden = 0;
  563. f.refresh();
  564. } else {
  565. f.df.hidden = 1;
  566. f.refresh();
  567. }
  568. }
  569. }
  570. }
  571. // setnewdoc is called when a record is loaded for the first time
  572. // ======================================================================================
  573. _f.Frm.prototype.setnewdoc = function(docname) {
  574. // moved this call to refresh function
  575. // this.check_doctype_conflict(docname);
  576. // if loaded
  577. if(this.opendocs[docname]) { // already exists
  578. this.docname=docname;
  579. return;
  580. }
  581. // make a copy of the doctype for client script settings
  582. // each record will have its own client script
  583. wn.meta.make_docfield_copy_for(this.doctype,docname);
  584. this.docname = docname;
  585. var me = this;
  586. var viewname = this.meta.issingle ? this.doctype : docname;
  587. // Client Script
  588. this.runclientscript('onload', this.doctype, this.docname);
  589. this.last_view_is_edit[docname] = 1;
  590. if(cint(this.meta.read_only_onload)) this.last_view_is_edit[docname] = 0;
  591. this.opendocs[docname] = true;
  592. }
  593. _f.Frm.prototype.edit_doc = function() {
  594. // set fields
  595. this.last_view_is_edit[this.docname] = true;
  596. this.refresh();
  597. }
  598. _f.Frm.prototype.show_doc = function(dn) {
  599. this.refresh(dn);
  600. }
  601. _f.Frm.prototype.runscript = function(scriptname, callingfield, onrefresh) {
  602. var me = this;
  603. if(this.docname) {
  604. // make doc list
  605. var doclist = wn.model.compress(make_doclist(this.doctype, this.docname));
  606. // send to run
  607. if(callingfield)
  608. $(callingfield.input).set_working();
  609. $c('runserverobj', {'docs':doclist, 'method':scriptname },
  610. function(r, rtxt) {
  611. // run refresh
  612. if(onrefresh)
  613. onrefresh(r,rtxt);
  614. // fields
  615. me.refresh_fields();
  616. // dependent fields
  617. me.refresh_dependency();
  618. // enable button
  619. if(callingfield)
  620. $(callingfield.input).done_working();
  621. }
  622. );
  623. }
  624. }
  625. _f.Frm.prototype.runclientscript = function(caller, cdt, cdn) {
  626. if(!cdt)cdt = this.doctype;
  627. if(!cdn)cdn = this.docname;
  628. var ret = null;
  629. var doc = locals[cur_frm.doc.doctype][cur_frm.doc.name];
  630. try {
  631. if(this.cscript[caller])
  632. ret = this.cscript[caller](doc, cdt, cdn);
  633. if(this.cscript['custom_'+caller])
  634. ret += this.cscript['custom_'+caller](doc, cdt, cdn);
  635. } catch(e) {
  636. validated = false;
  637. // show error message
  638. this.log_error(caller, e);
  639. }
  640. if(caller && caller.toLowerCase()=='setup') {
  641. this.setup_client_js();
  642. }
  643. return ret;
  644. }
  645. _f.Frm.prototype.setup_client_js = function(caller, cdt, cdn) {
  646. var doctype = wn.model.get_doc('DocType', this.doctype);
  647. // js
  648. var cs = doctype.__js || (doctype.client_script_core + doctype.client_script);
  649. if(cs) {
  650. try {
  651. var tmp = eval(cs);
  652. } catch(e) {
  653. show_alert("Error in Client Script.");
  654. this.log_error(caller || "setup_client_js", e);
  655. }
  656. }
  657. // css
  658. if(doctype.__css) set_style(doctype.__css)
  659. // ---Client String----
  660. if(doctype.client_string) { // split client string
  661. this.cstring = {};
  662. var elist = doctype.client_string.split('---');
  663. for(var i=1;i<elist.length;i=i+2) {
  664. this.cstring[strip(elist[i])] = elist[i+1];
  665. }
  666. }
  667. }
  668. _f.Frm.prototype.log_error = function(caller, e) {
  669. console.group && console.group();
  670. console.log("----- error in client script -----");
  671. console.log("method: " + caller);
  672. console.log(e);
  673. console.log("error message: " + e.message);
  674. console.trace && console.trace();
  675. console.log("----- end of error message -----");
  676. console.group && console.groupEnd();
  677. }
  678. _f.Frm.prototype.copy_doc = function(onload, from_amend) {
  679. if(!this.perm[0][CREATE]) {
  680. msgprint('You are not allowed to create '+this.meta.name);
  681. return;
  682. }
  683. var dn = this.docname;
  684. // copy parent
  685. var newdoc = wn.model.copy_doc(this.doctype, dn, from_amend);
  686. // do not copy attachments
  687. if(this.meta.allow_attach && newdoc.file_list && !from_amend)
  688. newdoc.file_list = null;
  689. // copy chidren
  690. var dl = make_doclist(this.doctype, dn);
  691. // table fields dict - for no_copy check
  692. var tf_dict = {};
  693. for(var d in dl) {
  694. d1 = dl[d];
  695. // get tabel field
  696. if(d1.parentfield && !tf_dict[d1.parentfield]) {
  697. tf_dict[d1.parentfield] = wn.meta.get_docfield(d1.parenttype, d1.parentfield);
  698. }
  699. if(d1.parent==dn && cint(tf_dict[d1.parentfield].no_copy)!=1) {
  700. var ch = wn.model.copy_doc(d1.doctype, d1.name, from_amend);
  701. ch.parent = newdoc.name;
  702. ch.docstatus = 0;
  703. ch.owner = user;
  704. ch.creation = '';
  705. ch.modified_by = user;
  706. ch.modified = '';
  707. }
  708. }
  709. newdoc.__islocal = 1;
  710. newdoc.docstatus = 0;
  711. newdoc.owner = user;
  712. newdoc.creation = '';
  713. newdoc.modified_by = user;
  714. newdoc.modified = '';
  715. if(onload)onload(newdoc);
  716. loaddoc(newdoc.doctype, newdoc.name);
  717. }
  718. _f.Frm.prototype.reload_doc = function() {
  719. this.check_doctype_conflict(this.docname);
  720. var me = this;
  721. var onsave = function(r, rtxt) {
  722. // n tweets and last comment
  723. //me.runclientscript('setup', me.doctype, me.docname);
  724. me.refresh();
  725. }
  726. if(me.doc.__islocal) {
  727. wn.call({
  728. method: "webnotes.widgets.form.load.getdoctype",
  729. args: {
  730. doctype: me.doctype
  731. },
  732. callback: function(r) {
  733. me.refresh();
  734. }
  735. })
  736. } else {
  737. wn.call({
  738. method: "webnotes.widgets.form.load.getdoc",
  739. args: {
  740. doctype: me.doctype,
  741. name: me.docname
  742. },
  743. callback: function(r) {
  744. me.refresh();
  745. }
  746. });
  747. }
  748. }
  749. var validated;
  750. _f.Frm.prototype.save = function(save_action, callback, btn, on_error) {
  751. $(document.activeElement).blur();
  752. var me = this;
  753. if((!this.meta.in_dialog || this.in_form) && !this.meta.istable)
  754. scroll(0, 0);
  755. // validate
  756. if(save_action!="Cancel") {
  757. validated = true;
  758. this.runclientscript('validate');
  759. if(!validated) {
  760. if(on_error)
  761. on_error();
  762. return;
  763. }
  764. }
  765. var doclist = new wn.model.DocList(this.doctype, this.docname);
  766. doclist.save(save_action || "Save", function(r) {
  767. if(!r.exc) {
  768. me.refresh();
  769. if(save_action==="Save") {
  770. me.runclientscript("after_save", me.doctype, me.docname);
  771. }
  772. } else {
  773. if(on_error)
  774. on_error();
  775. }
  776. callback && callback(r);
  777. }, btn);
  778. }
  779. _f.Frm.prototype.savesubmit = function(btn, on_error) {
  780. var me = this;
  781. wn.confirm("Permanently Submit "+this.docname+"?", function() {
  782. me.save('Submit', function(r) {
  783. if(!r.exc) {
  784. me.runclientscript('on_submit', me.doctype, me.docname);
  785. }
  786. }, btn, on_error);
  787. });
  788. }
  789. _f.Frm.prototype.savecancel = function(btn, on_error) {
  790. var me = this;
  791. wn.confirm("Permanently Cancel "+this.docname+"?", function() {
  792. me.runclientscript("before_cancel", me.doctype, me.docname);
  793. var doclist = new wn.model.DocList(me.doctype, me.docname);
  794. doclist.cancel(function(r) {
  795. if(!r.exc) {
  796. me.refresh();
  797. me.runclientscript("after_cancel", me.doctype, me.docname);
  798. }
  799. }, btn, on_error);
  800. });
  801. }
  802. // delete the record
  803. _f.Frm.prototype.savetrash = function() {
  804. wn.model.delete_doc(this.doctype, this.docname, function(r) {
  805. window.history.back();
  806. })
  807. }
  808. _f.Frm.prototype.amend_doc = function() {
  809. if(!this.fields_dict['amended_from']) {
  810. alert('"amended_from" field must be present to do an amendment.');
  811. return;
  812. }
  813. var me = this;
  814. var fn = function(newdoc) {
  815. newdoc.amended_from = me.docname;
  816. if(me.fields_dict && me.fields_dict['amendment_date'])
  817. newdoc.amendment_date = dateutil.obj_to_str(new Date());
  818. }
  819. this.copy_doc(fn, 1);
  820. }
  821. _f.Frm.prototype.disable_save = function() {
  822. // IMPORTANT: this function should be called in refresh event
  823. cur_frm.save_disabled = true;
  824. cur_frm.page_layout.footer.hide_save();
  825. cur_frm.frm_head.appframe.buttons.Save.remove();
  826. }
  827. _f.get_value = function(dt, dn, fn) {
  828. if(locals[dt] && locals[dt][dn])
  829. return locals[dt][dn][fn];
  830. }
  831. _f.Frm.prototype.set_value_in_locals = function(dt, dn, fn, v) {
  832. var d = locals[dt][dn];
  833. if (!d) return;
  834. var changed = d[fn] != v;
  835. if(changed && (d[fn]==null || v==null) && (cstr(d[fn])==cstr(v)))
  836. changed = false;
  837. if(changed) {
  838. d[fn] = v;
  839. if(d.parenttype)
  840. d.__unsaved = 1;
  841. this.set_unsaved();
  842. }
  843. }
  844. _f.Frm.prototype.set_unsaved = function() {
  845. if(cur_frm.doc.__unsaved)
  846. return;
  847. cur_frm.doc.__unsaved = 1;
  848. var frm_head;
  849. if(cur_frm.frm_head) {
  850. frm_head = cur_frm.frm_head;
  851. } else if(wn.container.page.frm && wn.container.page.frm.frm_head) {
  852. frm_head = wn.container.page.frm.frm_head
  853. }
  854. if(frm_head) frm_head.refresh_labels();
  855. }
  856. _f.Frm.prototype.show_comments = function() {
  857. if(!cur_frm.comments) {
  858. cur_frm.comments = new Dialog(540, 400, 'Comments');
  859. cur_frm.comments.comment_body = $a(cur_frm.comments.body, 'div', 'dialog_frm');
  860. $y(cur_frm.comments.body, {backgroundColor:'#EEE'});
  861. cur_frm.comments.list = new CommentList(cur_frm.comments.comment_body);
  862. }
  863. cur_frm.comments.list.dt = cur_frm.doctype;
  864. cur_frm.comments.list.dn = cur_frm.docname;
  865. cur_frm.comments.show();
  866. cur_frm.comments.list.run();
  867. }