Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

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