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.
 
 
 
 
 
 

969 lines
24 KiB

  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. }