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.

print_format.js 17 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  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. // default print style
  23. _p.def_print_style_body = "html, body, div, span, td { \
  24. font-family: Arial, Helvetica; font-size: 12px; }\
  25. pre { margin:0; padding:0;}"
  26. _p.def_print_style_other = "\n.simpletable, .noborder { \
  27. border-collapse: collapse; margin-bottom: 10px;}\
  28. .simpletable td {border: 1pt solid #777; vertical-align: top; padding: 4px; }\
  29. .noborder td { vertical-align: top; }"
  30. _p.go = function(html) {
  31. var d = document.createElement('div')
  32. d.innerHTML = html
  33. $(d).printElement();
  34. }
  35. _p.preview = function(html) {
  36. var w = window.open('');
  37. if(!w) return;
  38. w.document.write(html)
  39. w.document.close();
  40. }
  41. // _p can be referenced as this inside $.extend
  42. $.extend(_p, {
  43. show_dialog: function() {
  44. if(!_p.dialog) {
  45. _p.make_dialog();
  46. }
  47. _p.dialog.show();
  48. },
  49. make_dialog: function() {
  50. // Prepare Dialog Box Layout
  51. var d = new Dialog(
  52. 360, // w
  53. 140, // h
  54. 'Print Formats', // title
  55. [ // content
  56. ['HTML', 'Select'],
  57. ['Check', 'No Letterhead'],
  58. ['HTML', 'Buttons']
  59. ]);
  60. //d.widgets['No Letterhead'].checked = 1;
  61. // Print Button
  62. $btn(d.widgets.Buttons, 'Print', function() {
  63. _p.build(
  64. sel_val(cur_frm.print_sel), // fmtname
  65. _p.go, // onload
  66. d.widgets['No Letterhead'].checked // no_letterhead
  67. );
  68. },
  69. {
  70. cssFloat: 'right',
  71. marginBottom: '16px',
  72. marginLeft: '7px'
  73. }, 'green');
  74. // Print Preview
  75. $btn(d.widgets.Buttons, 'Preview', function() {
  76. _p.build(
  77. sel_val(cur_frm.print_sel), // fmtname
  78. _p.preview, // onload
  79. d.widgets['No Letterhead'].checked // no_letterhead
  80. );
  81. },
  82. {
  83. cssFloat: 'right',
  84. marginBottom: '16px'
  85. }, '');
  86. // Delete previous print format select list and Reload print format list from current form
  87. d.onshow = function() {
  88. var c = _p.dialog.widgets['Select'];
  89. if(c.cur_sel && c.cur_sel.parentNode == c) {
  90. c.removeChild(c.cur_sel);
  91. }
  92. c.appendChild(cur_frm.print_sel);
  93. c.cur_sel = cur_frm.print_sel;
  94. }
  95. _p.dialog = d;
  96. },
  97. // Define formats dict
  98. formats: {},
  99. /* args dict can contain:
  100. + fmtname --> print format name
  101. + onload
  102. + no_letterhead
  103. + only_body
  104. */
  105. build: function(fmtname, onload, no_letterhead, only_body) {
  106. if(!fmtname) {
  107. fmtname= "Standard";
  108. }
  109. args = {
  110. fmtname: fmtname,
  111. onload: onload,
  112. no_letterhead: no_letterhead,
  113. only_body: only_body
  114. };
  115. if(!cur_frm) {
  116. alert('No Document Selected');
  117. return;
  118. }
  119. // Get current doc (record)
  120. var doc = locals[cur_frm.doctype][cur_frm.docname];
  121. if(args.fmtname == 'Standard') {
  122. args.onload(_p.render({
  123. body: _p.print_std(args.no_letterhead),
  124. style: _p.print_style,
  125. doc: doc,
  126. title: doc.name,
  127. no_letterhead: args.no_letterhead,
  128. only_body: args.only_body
  129. }));
  130. } else {
  131. var print_format_doc = locals["Print Format"][args.fmtname];
  132. if(!print_format_doc) {
  133. msgprint("Unknown Print Format: " + args.fmtname);
  134. return;
  135. }
  136. args.onload(_p.render({
  137. body: print_format_doc.html,
  138. style: '',
  139. doc: doc,
  140. title: doc.name,
  141. no_letterhead: args.no_letterhead,
  142. only_body: args.only_body
  143. }));
  144. }
  145. },
  146. render: function(args) {
  147. var container = document.createElement('div');
  148. var stat = '';
  149. // if draft/archived, show draft/archived banner
  150. stat += _p.show_draft(args);
  151. stat += _p.show_archived(args);
  152. stat += _p.show_cancelled(args);
  153. // Append args.body's content as a child of container
  154. container.innerHTML = args.body;
  155. // Show letterhead?
  156. _p.show_letterhead(container, args);
  157. _p.run_embedded_js(container, args.doc);
  158. var style = _p.consolidate_css(container, args);
  159. _p.render_header_on_break(container, args);
  160. return _p.render_final(style, stat, container, args);
  161. },
  162. head_banner_format: function() {
  163. return "\
  164. <div style = '\
  165. text-align: center; \
  166. padding: 8px; \
  167. background-color: #CCC;'> \
  168. <div style = '\
  169. font-size: 20px; \
  170. font-weight: bold;'>\
  171. {{HEAD}}\
  172. </div>\
  173. {{DESCRIPTION}}\
  174. </div>"
  175. },
  176. /*
  177. Check if doc's status is not submitted (docstatus == 0)
  178. and submission is pending
  179. Display draft in header if true
  180. */
  181. show_draft: function(args) {
  182. var is_doctype_submittable = 0;
  183. var plist = locals['DocPerm'];
  184. for(var perm in plist) {
  185. var p = plist[perm];
  186. if((p.parent==args.doc.doctype) && (p.submit==1)){
  187. is_doctype_submittable = 1;
  188. break;
  189. }
  190. }
  191. if(args.doc && cint(args.doc.docstatus)==0 && is_doctype_submittable) {
  192. draft = _p.head_banner_format();
  193. draft = draft.replace("{{HEAD}}", "DRAFT");
  194. draft = draft.replace("{{DESCRIPTION}}", "This box will go away after the document is submitted.");
  195. return draft;
  196. } else {
  197. return "";
  198. }
  199. },
  200. /*
  201. Check if doc is archived
  202. Display archived in header if true
  203. */
  204. show_archived: function(args) {
  205. if(args.doc && args.doc.__archived) {
  206. archived = _p.head_banner_format();
  207. archived = archived.replace("{{HEAD}}", "ARCHIVED");
  208. archived = archived.replace("{{DESCRIPTION}}", "You must restore this document to make it editable.");
  209. return archived;
  210. } else {
  211. return "";
  212. }
  213. },
  214. /*
  215. Check if doc is cancelled
  216. Display cancelled in header if true
  217. */
  218. show_cancelled: function(args) {
  219. if(args.doc && args.doc.docstatus==2) {
  220. cancelled = _p.head_banner_format();
  221. cancelled = cancelled.replace("{{HEAD}}", "CANCELLED");
  222. cancelled = cancelled.replace("{{DESCRIPTION}}", "You must amend this document to make it editable.");
  223. return cancelled;
  224. } else {
  225. return "";
  226. }
  227. },
  228. consolidate_css: function(container, args) {
  229. // Extract <style> content from container
  230. var body_style = '';
  231. var style_list = container.getElementsByTagName('style');
  232. while(style_list && style_list.length>0) {
  233. for(i in style_list) {
  234. if(style_list[i] && style_list[i].innerHTML) {
  235. body_style += style_list[i].innerHTML;
  236. var parent = style_list[i].parentNode;
  237. if(parent) {
  238. parent.removeChild(style_list[i]);
  239. } else {
  240. container.removeChild(style_list[i]);
  241. }
  242. }
  243. }
  244. style_list = container.getElementsByTagName('style');
  245. }
  246. // Concatenate all styles
  247. //style_concat = _p.def_print_style_other + args.style + body_style;
  248. style_concat = (args.only_body ? '' : _p.def_print_style_body)
  249. + _p.def_print_style_other + args.style + body_style;
  250. return style_concat;
  251. },
  252. // This is used to calculate and substitude values in the HTML
  253. run_embedded_js: function(container, doc) {
  254. $(container).find("script").each(function(element) {
  255. var code = this.innerHTML;
  256. var new_html = code ? (eval(code) || "") : "";
  257. if(typeof new_html=="string")
  258. $(this).replaceWith(new_html);
  259. // var parent = jslist[i].parentNode;
  260. // var span = $a(parent, 'span');
  261. // parent.replaceChild(span, jslist[i]);
  262. // var val = code ? eval(code) : '';
  263. // if(!val || typeof(val)=='object') { val = ''; }
  264. // span.innerHTML = val;
  265. })
  266. },
  267. // Attach letterhead at top of container
  268. show_letterhead: function(container, args) {
  269. if(!(args.no_letterhead || args.only_body)) {
  270. container.innerHTML = '<div>' + _p.get_letter_head() + '</div>'
  271. + container.innerHTML;
  272. }
  273. },
  274. render_header_on_break: function(container, args) {
  275. var page_set = container.getElementsByClassName('page-settings');
  276. if(page_set.length) {
  277. for(var i = 0; i < page_set.length; i++) {
  278. var tmp = '';
  279. // if draft/archived, show draft/archived banner
  280. tmp += _p.show_draft(args);
  281. tmp += _p.show_archived(args);
  282. _p.show_letterhead(page_set[i], args);
  283. page_set[i].innerHTML = tmp + page_set[i].innerHTML;
  284. }
  285. }
  286. },
  287. // called by _p.render for final render of print
  288. render_final: function(style, stat, container, args) {
  289. var header = '<div class="page-settings">\n';
  290. var footer = '\n</div>';
  291. if(!args.only_body) {
  292. header = '<!DOCTYPE html>\n\
  293. <html>\
  294. <head>\
  295. <title>' + args.title + '</title>\
  296. <style>' + style + '</style>\
  297. </head>\
  298. <body>\n' + header;
  299. footer = footer + '\n</body>\n\
  300. </html>';
  301. }
  302. var finished = header
  303. + stat
  304. + container.innerHTML
  305. + footer;
  306. // replace relative links by absolute links
  307. var prefix = window.location.href.split("app.html")[0]
  308. $.each(finished.match(/src=['"]([^'"]*)['"]/) || [], function(i, v) {
  309. if(v.substr(0,4)!="src=") {
  310. if(v.substr(0,4)!="http")
  311. finished = finished.replace(v, prefix + v);
  312. }
  313. });
  314. return finished;
  315. },
  316. // fetches letter head from current doc or control panel
  317. get_letter_head: function() {
  318. var cp = wn.control_panel;
  319. var lh = '';
  320. if(cur_frm.doc.letter_head) {
  321. lh = cstr(wn.boot.letter_heads[cur_frm.doc.letter_head]);
  322. } else if (cp.letter_head) {
  323. lh = cp.letter_head;
  324. }
  325. return lh;
  326. },
  327. // common print style setting
  328. print_style: "\
  329. .datalabelcell { \
  330. padding: 2px 0px; \
  331. width: 38%; \
  332. vertical-align: top; \
  333. } \
  334. .datainputcell { \
  335. padding: 2px 0px; \
  336. width: 62%; \
  337. text-align: left; \
  338. }\
  339. .sectionHeading { \
  340. font-size: 16px; \
  341. font-weight: bold; \
  342. margin: 8px 0px; \
  343. } \
  344. .columnHeading { \
  345. font-size: 14px; \
  346. font-weight: bold; \
  347. margin: 8px 0px; \
  348. }",
  349. print_std: function(no_letterhead) {
  350. // Get doctype, docname, layout for a doctype
  351. var docname = cur_frm.docname;
  352. var doctype = cur_frm.doctype;
  353. var data = getchildren('DocField', doctype, 'fields', 'DocType');
  354. var layout = _p.add_layout(doctype);
  355. this.pf_list = [layout];
  356. var me = this;
  357. me.layout = layout;
  358. $.extend(this, {
  359. build_head: function(data, doctype, docname) {
  360. // Heading
  361. var h1_style = {
  362. fontSize: '22px',
  363. marginBottom: '8px'
  364. }
  365. var h1 = $a(me.layout.cur_row.header, 'h1', '', h1_style);
  366. // Get print heading
  367. if (cur_frm.pformat[docname]) {
  368. // first check in cur_frm.pformat
  369. h1.innerHTML = cur_frm.pformat[docname];
  370. } else {
  371. // then check if select print heading exists and has a value
  372. var val = null;
  373. for (var i = 0; i < data.length; i++) {
  374. if (data[i].fieldname === 'select_print_heading') {
  375. val = _f.get_value(doctype, docname, data[i].fieldname);
  376. break;
  377. }
  378. }
  379. // if not, just have doctype has heading
  380. h1.innerHTML = val ? val : get_doctype_label(doctype);
  381. }
  382. var h2_style = {
  383. fontSize: '16px',
  384. color: '#888',
  385. marginBottom: '8px',
  386. paddingBottom: '8px',
  387. borderBottom: (me.layout.with_border ? '0px' :
  388. '1px solid #000')
  389. }
  390. var h2 = $a(me.layout.cur_row.header, 'div', '', h2_style);
  391. h2.innerHTML = docname;
  392. },
  393. build_data: function(data, doctype, docname) {
  394. // Start with a row and a cell in that row
  395. if(data[0] && data[0].fieldtype != "Section Break") {
  396. me.layout.addrow();
  397. if(data[0].fieldtype != "Column Break") {
  398. me.layout.addcell();
  399. }
  400. }
  401. $.extend(this, {
  402. generate_custom_html: function(field, doctype, docname) {
  403. var container = $a(me.layout.cur_cell, 'div');
  404. container.innerHTML = cur_frm.pformat[field.fieldname](locals[doctype][docname]);
  405. },
  406. render_normal: function(field, data, i) {
  407. switch(field.fieldtype) {
  408. case 'Section Break':
  409. me.layout.addrow();
  410. // Add column if no column break after this field
  411. if(data[i+1] && data[i+1].fieldtype !=
  412. 'Column Break') {
  413. me.layout.addcell();
  414. }
  415. break;
  416. case 'Column Break':
  417. me.layout.addcell(field.width, field.label);
  418. break;
  419. case 'Table':
  420. var table = print_table(
  421. doctype, // dt
  422. docname, // dn
  423. field.fieldname,
  424. field.options, // tabletype
  425. null, // cols
  426. null, // head_labels
  427. null, // widths
  428. null); // condition
  429. me.layout = _p.print_std_add_table(table, me.layout, me.pf_list, doctype, no_letterhead);
  430. break;
  431. case 'HTML':
  432. var div = $a(me.layout.cur_cell, 'div');
  433. div.innerHTML = field.options;
  434. break;
  435. case 'Code':
  436. var div = $a(me.layout.cur_cell, 'div');
  437. var val = _f.get_value(doctype, docname,
  438. field.fieldname);
  439. div.innerHTML = '<div>' + field.label +
  440. ': </div><pre style="font-family: Courier, Fixed;">' + (val ? val : '') +
  441. '</pre>';
  442. break;
  443. case 'Text Editor':
  444. var div = $a(me.layout.cur_cell, 'div');
  445. var val = _f.get_value(doctype, docname,
  446. field.fieldname);
  447. div.innerHTML = val ? val : '';
  448. break;
  449. default:
  450. // Add Cell Data
  451. _p.print_std_add_field(doctype, docname, field, me.layout);
  452. break;
  453. }
  454. }
  455. });
  456. // Then build each field
  457. for(var i = 0; i < data.length; i++) {
  458. var fieldname = data[i].fieldname ? data[i].fieldname :
  459. data[i].label;
  460. var field = fieldname ?
  461. wn.meta.get_docfield(doctype, fieldname, docname) : data[i];
  462. if(!field.print_hide) {
  463. if(cur_frm.pformat[field.fieldname]) {
  464. // If there is a custom method to generate the HTML, then use it
  465. this.generate_custom_html(field, doctype, docname);
  466. } else {
  467. // Do the normal rendering
  468. this.render_normal(field, data, i);
  469. }
  470. }
  471. }
  472. me.layout.close_borders();
  473. },
  474. build_html: function() {
  475. var html = '';
  476. for(var i = 0; i < me.pf_list.length; i++) {
  477. if(me.pf_list[i].wrapper) {
  478. html += me.pf_list[i].wrapper.innerHTML;
  479. } else if(me.pf_list[i].innerHTML) {
  480. html += me.pf_list[i].innerHTML;
  481. } else {
  482. html += me.pf_list[i];
  483. }
  484. }
  485. this.pf_list = [];
  486. return html;
  487. }
  488. });
  489. this.build_head(data, doctype, docname);
  490. this.build_data(data, doctype, docname);
  491. var html = this.build_html();
  492. return html;
  493. },
  494. add_layout: function(doctype) {
  495. var layout = new Layout();
  496. layout.addrow();
  497. if(locals['DocType'][doctype].print_outline == 'Yes') {
  498. layout.with_border = 1
  499. }
  500. return layout;
  501. },
  502. print_std_add_table: function(t, layout, pf_list, dt, no_letterhead) {
  503. if(t.appendChild) {
  504. // If only one table is passed
  505. layout.cur_cell.appendChild(t);
  506. } else {
  507. page_break = '\n\
  508. <div style = "page-break-after: always;" \
  509. class = "page_break"></div><div class="page-settings"></div>';
  510. // If a list of tables is passed
  511. for(var i = 0; i < t.length-1; i++) {
  512. // add to current page
  513. layout.cur_cell.appendChild(t[i]);
  514. layout.close_borders();
  515. pf_list.push(page_break);
  516. // Create new page
  517. layout = _p.add_layout(dt, no_letterhead);
  518. pf_list.push(layout);
  519. layout.addrow();
  520. layout.addcell();
  521. var div = $a(layout.cur_cell, 'div');
  522. div.innerHTML = 'Continued from previous page...';
  523. div.style.padding = '4px';
  524. }
  525. // Append last table
  526. layout.cur_cell.appendChild(t[t.length-1]);
  527. }
  528. return layout;
  529. },
  530. print_std_add_field: function(dt, dn, f, layout) {
  531. var val = _f.get_value(dt, dn, f.fieldname);
  532. if(f.fieldtype!='Button') {
  533. if(val || in_list(['Float', 'Int', 'Currency'], f.fieldtype)) {
  534. // If value or a numeric type then proceed
  535. // Add field table
  536. row = _p.field_tab(layout.cur_cell);
  537. // Add label
  538. row.cells[0].innerHTML = f.label ? f.label : f.fieldname;
  539. $s(row.cells[1], val, f.fieldtype);
  540. // left align currency in normal display
  541. if(f.fieldtype == 'Currency') {
  542. $y(row.cells[1], { textAlign: 'left' });
  543. }
  544. }
  545. }
  546. },
  547. field_tab: function(layout_cell) {
  548. var tab = $a(layout_cell, 'table', '', {width:'100%'});
  549. var row = tab.insertRow(0);
  550. _p.row = row; // Don't know this line's purpose
  551. row.insertCell(0);
  552. row.insertCell(1);
  553. row.cells[0].className = 'datalabelcell';
  554. row.cells[1].className = 'datainputcell';
  555. return row;
  556. }
  557. });