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 19 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
12 years ago
12 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796
  1. // Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
  2. // MIT License. See license.txt
  3. /* Form page structure
  4. + this.parent (either FormContainer or Dialog)
  5. + this.wrapper
  6. + this.toolbar
  7. + this.form_wrapper
  8. + this.head
  9. + this.body
  10. + this.layout
  11. + this.sidebar
  12. + this.footer
  13. */
  14. wn.provide('_f');
  15. wn.provide('wn.ui.form');
  16. wn.ui.form.Controller = Class.extend({
  17. init: function(opts) {
  18. $.extend(this, opts);
  19. this.setup && this.setup();
  20. }
  21. });
  22. _f.frms = {};
  23. _f.Frm = function(doctype, parent, in_form) {
  24. this.docname = '';
  25. this.doctype = doctype;
  26. this.display = 0;
  27. this.refresh_if_stale_for = 120;
  28. var me = this;
  29. this.opendocs = {};
  30. this.sections = [];
  31. this.grids = [];
  32. this.cscript = new wn.ui.form.Controller({frm:this});
  33. this.pformat = {};
  34. this.fetch_dict = {};
  35. this.parent = parent;
  36. this.tinymce_id_list = [];
  37. this.setup_meta(doctype);
  38. // show in form instead of in dialog, when called using url (router.js)
  39. this.in_form = in_form ? true : false;
  40. // notify on rename
  41. var me = this;
  42. $(document).on('rename', function(event, dt, old_name, new_name) {
  43. if(dt==me.doctype)
  44. me.rename_notify(dt, old_name, new_name)
  45. });
  46. }
  47. _f.Frm.prototype.check_doctype_conflict = function(docname) {
  48. var me = this;
  49. if(this.doctype=='DocType' && docname=='DocType') {
  50. msgprint('Allowing DocType, DocType. Be careful!')
  51. } else if(this.doctype=='DocType') {
  52. if (wn.views.formview[docname] || wn.pages['List/'+docname]) {
  53. msgprint("Cannot open DocType when its instance is open")
  54. throw 'doctype open conflict'
  55. }
  56. } else {
  57. if (wn.views.formview.DocType && wn.views.formview.DocType.frm.opendocs[this.doctype]) {
  58. msgprint("Cannot open instance when its DocType is open")
  59. throw 'doctype open conflict'
  60. }
  61. }
  62. }
  63. _f.Frm.prototype.setup = function() {
  64. var me = this;
  65. this.fields = [];
  66. this.fields_dict = {};
  67. this.state_fieldname = wn.workflow.get_state_fieldname(this.doctype);
  68. // wrapper
  69. this.wrapper = this.parent;
  70. wn.ui.make_app_page({
  71. parent: this.wrapper,
  72. single_column: true
  73. });
  74. this.appframe = this.wrapper.appframe;
  75. this.layout_main = $(this.wrapper)
  76. .find(".layout-main")
  77. .css({"padding-bottom": "0px"})
  78. .get(0);
  79. this.toolbar = new wn.ui.form.Toolbar({
  80. frm: this,
  81. appframe: this.appframe
  82. });
  83. this.frm_head = this.toolbar;
  84. // print layout
  85. this.setup_print_layout();
  86. // 2 column layout
  87. this.setup_std_layout();
  88. // client script must be called after "setup" - there are no fields_dict attached to the frm otherwise
  89. this.script_manager = new wn.ui.form.ScriptManager({
  90. frm: this
  91. });
  92. this.script_manager.setup();
  93. this.watch_model_updates();
  94. this.footer = new wn.ui.form.Footer({
  95. frm: this,
  96. parent: $(this.wrapper).find(".appframe-footer")
  97. })
  98. this.setup_done = true;
  99. }
  100. _f.Frm.prototype.setup_print_layout = function() {
  101. this.print_wrapper = $('<div>\
  102. <div class="print-toolbar row" style="background-color: #eee; \
  103. padding-top: 5px; padding-bottom: 5px; margin-top: -15px; \
  104. margin-bottom: 15px; padding-left: 15px; position:relative;">\
  105. <i class="text-muted icon-print" style="position: absolute; top: 13px; left: 10px; "></i>\
  106. <div class="col-xs-3">\
  107. <select class="print-preview-select form-control"></select></div>\
  108. <div class="col-xs-3" style="padding-top: 7px;">\
  109. <input type="checkbox" class="print-letterhead" checked/> Letterhead</div>\
  110. <div class="col-xs-6 text-right" style="padding-top: 7px;">\
  111. <a style="margin-right: 7px;" class="print-print">Print</a>\
  112. <a class="close">×</a>\
  113. </div>\
  114. </div>\
  115. <div class="print-preview">\
  116. </div>\
  117. </div>')
  118. .appendTo(this.layout_main)
  119. .toggle(false);
  120. var me = this;
  121. this.print_wrapper.find(".close").click(function() {
  122. me.hide_print();
  123. });
  124. this.print_formats = wn.meta.get_print_formats(this.meta.name);
  125. this.print_letterhead = this.print_wrapper
  126. .find(".print-letterhead")
  127. .on("change", function() { me.print_sel.trigger("change"); });
  128. this.print_sel = this.print_wrapper
  129. .find(".print-preview-select")
  130. .on("change", function() {
  131. _p.build(me.print_sel.val(), function(html) {
  132. me.print_wrapper.find(".print-preview").html(html);
  133. }, !me.print_letterhead.is(":checked"), true, true);
  134. })
  135. this.print_wrapper.find(".print-print").click(function() {
  136. _p.build(
  137. me.print_sel.val(), // fmtname
  138. _p.go, // onload
  139. !me.print_letterhead.is(":checked") // no_letterhead
  140. );
  141. })
  142. }
  143. _f.Frm.prototype.print_doc = function() {
  144. if(this.print_wrapper.is(":visible")) {
  145. this.hide_print();
  146. return;
  147. }
  148. if(this.doc.docstatus==2) {
  149. msgprint("Cannot Print Cancelled Documents.");
  150. return;
  151. }
  152. this.print_wrapper.toggle(true);
  153. this.print_sel
  154. .empty().add_options(this.print_formats)
  155. .trigger("change");
  156. this.form_wrapper.toggle(false);
  157. }
  158. _f.Frm.prototype.hide_print = function() {
  159. if(this.setup_done) {
  160. this.print_wrapper.toggle(false);
  161. this.form_wrapper.toggle(true);
  162. }
  163. }
  164. _f.Frm.prototype.watch_model_updates = function() {
  165. // watch model updates
  166. var me = this;
  167. // on main doc
  168. wn.model.on(me.doctype, "*", function(fieldname, value, doc) {
  169. // set input
  170. if(doc.name===me.docname) {
  171. me.dirty();
  172. me.fields_dict[fieldname]
  173. && me.fields_dict[fieldname].refresh(fieldname);
  174. me.refresh_dependency();
  175. me.script_manager.trigger(fieldname, doc.doctype, doc.name);
  176. }
  177. })
  178. // on table fields
  179. $.each(wn.model.get("DocField", {fieldtype:"Table", parent: me.doctype}), function(i, df) {
  180. wn.model.on(df.options, "*", function(fieldname, value, doc) {
  181. if(doc.parent===me.docname && doc.parentfield===df.fieldname) {
  182. me.dirty();
  183. me.fields_dict[df.fieldname].grid.set_value(fieldname, value, doc);
  184. me.script_manager.trigger(fieldname, doc.doctype, doc.name);
  185. }
  186. })
  187. })
  188. }
  189. _f.Frm.prototype.onhide = function() {
  190. if(_f.cur_grid_cell) _f.cur_grid_cell.grid.cell_deselect();
  191. }
  192. _f.Frm.prototype.setup_std_layout = function() {
  193. this.form_wrapper = $('<div></div>').appendTo(this.layout_main);
  194. this.body_header = $("<div>").appendTo(this.form_wrapper);
  195. this.body = $("<div>").appendTo(this.form_wrapper);
  196. // only tray
  197. this.meta.section_style='Simple'; // always simple!
  198. // layout
  199. this.layout = new wn.ui.form.Layout({
  200. parent: this.body,
  201. doctype: this.doctype,
  202. frm: this,
  203. });
  204. this.layout.make();
  205. this.fields_dict = this.layout.fields_dict;
  206. this.fields = this.layout.fields_list;
  207. this.dashboard = new wn.ui.form.Dashboard({
  208. frm: this,
  209. });
  210. // state
  211. this.states = new wn.ui.form.States({
  212. frm: this
  213. });
  214. }
  215. // email the form
  216. _f.Frm.prototype.email_doc = function(message) {
  217. new wn.views.CommunicationComposer({
  218. doc: this.doc,
  219. subject: wn._(this.meta.name) + ': ' + this.docname,
  220. recipients: this.doc.email || this.doc.email_id || this.doc.contact_email,
  221. attach_document_print: true,
  222. message: message,
  223. real_name: this.doc.real_name || this.doc.contact_display || this.doc.contact_name
  224. });
  225. }
  226. // rename the form
  227. _f.Frm.prototype.rename_doc = function() {
  228. wn.model.rename_doc(this.doctype, this.docname);
  229. }
  230. // notify this form of renamed records
  231. _f.Frm.prototype.rename_notify = function(dt, old, name) {
  232. // from form
  233. if(this.meta.istable)
  234. return;
  235. if(this.docname == old)
  236. this.docname = name;
  237. else
  238. return;
  239. // cleanup
  240. if(this && this.opendocs[old]) {
  241. // delete docfield copy
  242. wn.meta.docfield_copy[dt][name] = wn.meta.docfield_copy[dt][old];
  243. delete wn.meta.docfield_copy[dt][old];
  244. }
  245. delete this.opendocs[old];
  246. this.opendocs[name] = true;
  247. if(this.meta.in_dialog || !this.in_form) {
  248. return;
  249. }
  250. wn.re_route[window.location.hash] = '#Form/' + encodeURIComponent(this.doctype) + '/' + encodeURIComponent(name);
  251. wn.set_route('Form', this.doctype, name);
  252. }
  253. // SETUP
  254. _f.Frm.prototype.setup_meta = function(doctype) {
  255. this.meta = wn.model.get_doc('DocType',this.doctype);
  256. this.perm = wn.perm.get_perm(this.doctype); // for create
  257. if(this.meta.istable) { this.meta.in_dialog = 1 }
  258. }
  259. _f.Frm.prototype.defocus_rest = function() {
  260. // deselect others
  261. if(_f.cur_grid_cell) _f.cur_grid_cell.grid.cell_deselect();
  262. }
  263. _f.Frm.prototype.refresh_header = function() {
  264. // set title
  265. // main title
  266. if(!this.meta.in_dialog || this.in_form) {
  267. set_title(this.meta.issingle ? this.doctype : this.docname);
  268. }
  269. if(wn.ui.toolbar.recent)
  270. wn.ui.toolbar.recent.add(this.doctype, this.docname, 1);
  271. // show / hide buttons
  272. if(this.frm_head) {
  273. this.frm_head.refresh();
  274. }
  275. }
  276. _f.Frm.prototype.check_doc_perm = function() {
  277. // get perm
  278. var dt = this.parent_doctype?this.parent_doctype : this.doctype;
  279. var dn = this.parent_docname?this.parent_docname : this.docname;
  280. this.perm = wn.perm.get_perm(dt, dn);
  281. if(!this.perm[0][READ]) {
  282. return 0;
  283. }
  284. return 1
  285. }
  286. _f.Frm.prototype.refresh = function(docname) {
  287. // record switch
  288. if(docname) {
  289. if(this.docname != docname && (!this.meta.in_dialog || this.in_form) &&
  290. !this.meta.istable) {
  291. scroll(0, 0);
  292. this.hide_print();
  293. }
  294. this.docname = docname;
  295. }
  296. cur_frm = this;
  297. if(this.docname) { // document to show
  298. // check permissions
  299. if(!this.check_doc_perm()) return;
  300. // read only (workflow)
  301. this.read_only = wn.workflow.is_read_only(this.doctype, this.docname);
  302. // set the doc
  303. this.doc = wn.model.get_doc(this.doctype, this.docname);
  304. // check if doctype is already open
  305. if (!this.opendocs[this.docname]) {
  306. this.check_doctype_conflict(this.docname);
  307. } else {
  308. if(this.doc && (!this.doc.__unsaved) && this.doc.__last_sync_on &&
  309. (new Date() - this.doc.__last_sync_on) > (this.refresh_if_stale_for * 1000)) {
  310. this.reload_doc();
  311. return;
  312. }
  313. }
  314. // do setup
  315. if(!this.setup_done) this.setup();
  316. // load the record for the first time, if not loaded (call 'onload')
  317. cur_frm.cscript.is_onload = false;
  318. if(!this.opendocs[this.docname]) {
  319. cur_frm.cscript.is_onload = true;
  320. this.setnewdoc();
  321. } else {
  322. this.render_form();
  323. }
  324. }
  325. }
  326. _f.Frm.prototype.render_form = function() {
  327. if(!this.meta.istable) {
  328. // header
  329. this.refresh_header();
  330. // call trigger
  331. this.script_manager.trigger("refresh");
  332. // trigger global trigger
  333. // to use this
  334. $(document).trigger('form_refresh');
  335. // fields
  336. this.refresh_fields();
  337. // call onload post render for callbacks to be fired
  338. if(this.cscript.is_onload) {
  339. this.script_manager.trigger("onload_post_render");
  340. }
  341. // focus on first input
  342. if(this.doc.docstatus==0) {
  343. var first = this.form_wrapper.find('.form-layout-row :input:first');
  344. if(!in_list(["Date", "Datetime"], first.attr("data-fieldtype"))) {
  345. first.focus();
  346. }
  347. }
  348. } else {
  349. this.refresh_header();
  350. }
  351. $(cur_frm.wrapper).trigger('render_complete');
  352. }
  353. _f.Frm.prototype.refresh_field = function(fname) {
  354. cur_frm.fields_dict[fname] && cur_frm.fields_dict[fname].refresh
  355. && cur_frm.fields_dict[fname].refresh();
  356. }
  357. _f.Frm.prototype.refresh_fields = function() {
  358. this.layout.refresh();
  359. this.layout.primary_button = $(this.wrapper).find(".btn-primary");
  360. // cleanup activities after refresh
  361. this.cleanup_refresh(this);
  362. // dependent fields
  363. this.refresh_dependency();
  364. }
  365. _f.Frm.prototype.cleanup_refresh = function() {
  366. var me = this;
  367. if(me.fields_dict['amended_from']) {
  368. if (me.doc.amended_from) {
  369. unhide_field('amended_from');
  370. if (me.fields_dict['amendment_date']) unhide_field('amendment_date');
  371. } else {
  372. hide_field('amended_from');
  373. if (me.fields_dict['amendment_date']) hide_field('amendment_date');
  374. }
  375. }
  376. if(me.fields_dict['trash_reason']) {
  377. if(me.doc.trash_reason && me.doc.docstatus == 2) {
  378. unhide_field('trash_reason');
  379. } else {
  380. hide_field('trash_reason');
  381. }
  382. }
  383. if(me.meta.autoname && me.meta.autoname.substr(0,6)=='field:' && !me.doc.__islocal) {
  384. var fn = me.meta.autoname.substr(6);
  385. cur_frm.toggle_display(fn, false);
  386. }
  387. if(me.meta.autoname=="naming_series:" && !me.doc.__islocal) {
  388. cur_frm.toggle_display("naming_series", false);
  389. }
  390. }
  391. // Resolve "depends_on" and show / hide accordingly
  392. _f.Frm.prototype.refresh_dependency = function() {
  393. var me = this;
  394. var doc = locals[this.doctype][this.docname];
  395. // build dependants' dictionary
  396. var has_dep = false;
  397. for(fkey in me.fields) {
  398. var f = me.fields[fkey];
  399. f.dependencies_clear = true;
  400. if(f.df.depends_on) {
  401. has_dep = true;
  402. }
  403. }
  404. if(!has_dep)return;
  405. // show / hide based on values
  406. for(var i=me.fields.length-1;i>=0;i--) {
  407. var f = me.fields[i];
  408. f.guardian_has_value = true;
  409. if(f.df.depends_on) {
  410. // evaluate guardian
  411. var v = doc[f.df.depends_on];
  412. if(f.df.depends_on.substr(0,5)=='eval:') {
  413. f.guardian_has_value = eval(f.df.depends_on.substr(5));
  414. } else if(f.df.depends_on.substr(0,3)=='fn:') {
  415. f.guardian_has_value = me.script_manager.trigger(f.df.depends_on.substr(3), me.doctype, me.docname);
  416. } else {
  417. if(!v) {
  418. f.guardian_has_value = false;
  419. }
  420. }
  421. // show / hide
  422. if(f.guardian_has_value) {
  423. if(f.df.hidden != 0) {
  424. f.df.hidden = 0;
  425. f.refresh();
  426. }
  427. } else {
  428. if(f.df.hidden != 1) {
  429. f.df.hidden = 1;
  430. f.refresh();
  431. }
  432. }
  433. }
  434. }
  435. this.layout.refresh_section_count();
  436. }
  437. _f.Frm.prototype.setnewdoc = function() {
  438. // moved this call to refresh function
  439. // this.check_doctype_conflict(docname);
  440. var me = this;
  441. this.script_manager.trigger("before_load", this.doctype, this.docname, function() {
  442. me.script_manager.trigger("onload");
  443. me.opendocs[me.docname] = true;
  444. me.render_form();
  445. })
  446. }
  447. _f.Frm.prototype.runscript = function(scriptname, callingfield, onrefresh) {
  448. var me = this;
  449. if(this.docname) {
  450. // make doc list
  451. var doclist = wn.model.compress(make_doclist(this.doctype, this.docname));
  452. // send to run
  453. if(callingfield)
  454. $(callingfield.input).set_working();
  455. return $c('runserverobj', {'docs':doclist, 'method':scriptname },
  456. function(r, rtxt) {
  457. // run refresh
  458. if(onrefresh)
  459. onrefresh(r,rtxt);
  460. // fields
  461. me.refresh_fields();
  462. // enable button
  463. if(callingfield)
  464. $(callingfield.input).done_working();
  465. }
  466. );
  467. }
  468. }
  469. _f.Frm.prototype.copy_doc = function(onload, from_amend) {
  470. if(!this.perm[0][CREATE]) {
  471. msgprint('You are not allowed to create '+this.meta.name);
  472. return;
  473. }
  474. var dn = this.docname;
  475. // copy parent
  476. var newdoc = wn.model.copy_doc(this.doctype, dn, from_amend);
  477. // copy chidren
  478. var dl = make_doclist(this.doctype, dn);
  479. // table fields dict - for no_copy check
  480. var tf_dict = {};
  481. for(var d in dl) {
  482. d1 = dl[d];
  483. // get tabel field
  484. if(d1.parentfield && !tf_dict[d1.parentfield]) {
  485. tf_dict[d1.parentfield] = wn.meta.get_docfield(d1.parenttype, d1.parentfield);
  486. }
  487. if(d1.parent==dn && cint(tf_dict[d1.parentfield].no_copy)!=1) {
  488. var ch = wn.model.copy_doc(d1.doctype, d1.name, from_amend);
  489. ch.parent = newdoc.name;
  490. ch.docstatus = 0;
  491. ch.owner = user;
  492. ch.creation = '';
  493. ch.modified_by = user;
  494. ch.modified = '';
  495. }
  496. }
  497. newdoc.__islocal = 1;
  498. newdoc.docstatus = 0;
  499. newdoc.owner = user;
  500. newdoc.creation = '';
  501. newdoc.modified_by = user;
  502. newdoc.modified = '';
  503. if(onload)onload(newdoc);
  504. loaddoc(newdoc.doctype, newdoc.name);
  505. }
  506. _f.Frm.prototype.reload_doc = function() {
  507. this.check_doctype_conflict(this.docname);
  508. var me = this;
  509. var onsave = function(r, rtxt) {
  510. me.refresh();
  511. }
  512. if(!me.doc.__islocal) {
  513. wn.model.remove_from_locals(me.doctype, me.docname);
  514. wn.model.with_doc(me.doctype, me.docname, function() {
  515. me.refresh();
  516. })
  517. }
  518. }
  519. var validated;
  520. _f.Frm.prototype.save = function(save_action, callback, btn, on_error) {
  521. $(document.activeElement).blur();
  522. var me = this;
  523. if((!this.meta.in_dialog || this.in_form) && !this.meta.istable)
  524. scroll(0, 0);
  525. // validate
  526. if(save_action!="Cancel") {
  527. validated = true;
  528. this.script_manager.trigger("validate");
  529. if(!validated) {
  530. if(on_error)
  531. on_error();
  532. return;
  533. }
  534. }
  535. var doclist = new wn.model.DocList(this.doctype, this.docname);
  536. doclist.save(save_action || "Save", function(r) {
  537. if(!r.exc) {
  538. me.refresh();
  539. } else {
  540. if(on_error)
  541. on_error();
  542. }
  543. callback && callback(r);
  544. if(wn._from_link) {
  545. if(me.doctype===wn._from_link.df.options) {
  546. wn._from_link.parse_validate_and_set_in_model(me.docname);
  547. wn.set_route("Form", wn._from_link.frm.doctype, wn._from_link.frm.docname);
  548. setTimeout(function() { scroll(0, wn._from_link_scrollY); }, 100);
  549. }
  550. wn._from_link = null;
  551. }
  552. }, btn);
  553. }
  554. _f.Frm.prototype.savesubmit = function(btn, on_error) {
  555. var me = this;
  556. wn.confirm("Permanently Submit "+this.docname+"?", function() {
  557. validated = true;
  558. me.script_manager.trigger("before_submit");
  559. if(!validated) {
  560. if(on_error)
  561. on_error();
  562. return;
  563. }
  564. me.save('Submit', function(r) {
  565. if(!r.exc) {
  566. me.script_manager.trigger("on_submit");
  567. }
  568. }, btn, on_error);
  569. });
  570. }
  571. _f.Frm.prototype.savecancel = function(btn, on_error) {
  572. var me = this;
  573. wn.confirm("Permanently Cancel "+this.docname+"?", function() {
  574. validated = true;
  575. me.script_manager.trigger("before_cancel");
  576. if(!validated) {
  577. if(on_error)
  578. on_error();
  579. return;
  580. }
  581. var doclist = new wn.model.DocList(me.doctype, me.docname);
  582. doclist.cancel(function(r) {
  583. if(!r.exc) {
  584. me.refresh();
  585. me.script_manager.trigger("after_cancel");
  586. }
  587. }, btn, on_error);
  588. });
  589. }
  590. // delete the record
  591. _f.Frm.prototype.savetrash = function() {
  592. wn.model.delete_doc(this.doctype, this.docname, function(r) {
  593. window.history.back();
  594. })
  595. }
  596. _f.Frm.prototype.amend_doc = function() {
  597. if(!this.fields_dict['amended_from']) {
  598. alert('"amended_from" field must be present to do an amendment.');
  599. return;
  600. }
  601. var me = this;
  602. var fn = function(newdoc) {
  603. newdoc.amended_from = me.docname;
  604. if(me.fields_dict && me.fields_dict['amendment_date'])
  605. newdoc.amendment_date = dateutil.obj_to_str(new Date());
  606. }
  607. this.copy_doc(fn, 1);
  608. }
  609. _f.Frm.prototype.disable_save = function() {
  610. // IMPORTANT: this function should be called in refresh event
  611. this.save_disabled = true;
  612. this.appframe.set_title_right("", null);
  613. }
  614. _f.Frm.prototype.save_or_update = function() {
  615. if(this.save_disabled) return;
  616. if(this.doc.docstatus===0) {
  617. this.save();
  618. } else if(this.doc.docstatus===1 && this.doc.__unsaved) {
  619. this.save("Update");
  620. }
  621. }
  622. _f.get_value = function(dt, dn, fn) {
  623. if(locals[dt] && locals[dt][dn])
  624. return locals[dt][dn][fn];
  625. }
  626. _f.Frm.prototype.dirty = function() {
  627. this.doc.__unsaved = 1;
  628. $(this.wrapper).trigger('dirty')
  629. }
  630. _f.Frm.prototype.get_docinfo = function() {
  631. return wn.model.docinfo[this.doctype][this.docname];
  632. }
  633. _f.Frm.prototype.get_perm = function(permlevel, access_type) {
  634. return this.perm[permlevel] ? this.perm[permlevel][access_type] : null;
  635. }
  636. _f.Frm.prototype.set_intro = function(txt) {
  637. wn.utils.set_intro(this, this.body, txt);
  638. }
  639. _f.Frm.prototype.set_footnote = function(txt) {
  640. wn.utils.set_footnote(this, this.body, txt);
  641. }
  642. _f.Frm.prototype.add_custom_button = function(label, fn, icon) {
  643. return this.appframe.add_primary_action(label, fn, icon || "icon-arrow-right");
  644. }
  645. _f.Frm.prototype.clear_custom_buttons = function() {
  646. this.toolbar.refresh()
  647. }
  648. _f.Frm.prototype.add_fetch = function(link_field, src_field, tar_field) {
  649. if(!this.fetch_dict[link_field]) {
  650. this.fetch_dict[link_field] = {'columns':[], 'fields':[]}
  651. }
  652. this.fetch_dict[link_field].columns.push(src_field);
  653. this.fetch_dict[link_field].fields.push(tar_field);
  654. }
  655. _f.Frm.prototype.set_print_heading = function(txt) {
  656. this.pformat[cur_frm.docname] = txt;
  657. }