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