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

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