您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

print_format.js 18 KiB

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