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

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