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

864 行
22 KiB

  1. frappe.pages['print'].on_page_load = function(wrapper) {
  2. frappe.ui.make_app_page({
  3. parent: wrapper,
  4. });
  5. let print_view = new frappe.ui.form.PrintView(wrapper);
  6. $(wrapper).bind('show', () => {
  7. const route = frappe.get_route();
  8. const doctype = route[1];
  9. const docname = route[2];
  10. if (!frappe.route_options || !frappe.route_options.frm) {
  11. frappe.model.with_doc(doctype, docname, () => {
  12. let frm = { doctype: doctype, docname: docname };
  13. frm.doc = frappe.get_doc(doctype, docname);
  14. frappe.model.with_doctype(doctype, () => {
  15. frm.meta = frappe.get_meta(route[1]);
  16. print_view.show(frm);
  17. });
  18. });
  19. } else {
  20. print_view.frm = frappe.route_options.frm;
  21. frappe.route_options.frm = null;
  22. print_view.show(print_view.frm);
  23. }
  24. });
  25. };
  26. frappe.ui.form.PrintView = class {
  27. constructor(wrapper) {
  28. this.wrapper = $(wrapper);
  29. this.page = wrapper.page;
  30. this.make();
  31. }
  32. make() {
  33. this.print_wrapper = this.page.main.empty().html(
  34. `<div class="print-preview-wrapper"><div class="print-preview">
  35. ${frappe.render_template('print_skeleton_loading')}
  36. <iframe class="print-format-container" width="100%" height="0" frameBorder="0" scrolling="no">
  37. </iframe>
  38. </div>
  39. <div class="page-break-message text-muted text-center text-medium margin-top"></div>
  40. </div>
  41. <div class="preview-beta-wrapper">
  42. <iframe width="100%" height="0" frameBorder="0"></iframe>
  43. </div>
  44. `
  45. );
  46. this.print_settings = frappe.model.get_doc(
  47. ':Print Settings',
  48. 'Print Settings'
  49. );
  50. this.setup_menu();
  51. this.setup_toolbar();
  52. this.setup_sidebar();
  53. this.setup_keyboard_shortcuts();
  54. }
  55. set_title() {
  56. this.page.set_title(this.frm.docname);
  57. }
  58. setup_toolbar() {
  59. this.page.set_primary_action(
  60. __('Print'),
  61. () => this.printit(), 'printer'
  62. );
  63. this.page.add_button(
  64. __('Full Page'),
  65. () => this.render_page('/printview?'),
  66. { icon: 'full-page' }
  67. );
  68. this.page.add_button(
  69. __('PDF'),
  70. () => this.render_pdf(),
  71. { icon: 'small-file' }
  72. );
  73. this.page.add_button(
  74. __('Refresh'),
  75. () => this.refresh_print_format(),
  76. { icon: 'refresh' }
  77. );
  78. }
  79. setup_sidebar() {
  80. this.sidebar = this.page.sidebar.addClass('print-preview-sidebar');
  81. this.print_sel = this.add_sidebar_item(
  82. {
  83. fieldtype: 'Select',
  84. fieldname: 'print_format',
  85. label: 'Print Format',
  86. options: [this.get_default_option_for_select(__('Select Print Format'))],
  87. change: () => this.refresh_print_format(),
  88. default: __('Select Print Format')
  89. },
  90. ).$input;
  91. this.language_sel = this.add_sidebar_item(
  92. {
  93. fieldtype: 'Select',
  94. fieldname: 'language',
  95. placeholder: 'Language',
  96. options: [
  97. this.get_default_option_for_select(__('Select Language')),
  98. ...this.get_language_options()
  99. ],
  100. default: __('Select Language'),
  101. change: () => {
  102. this.set_user_lang();
  103. this.preview();
  104. },
  105. },
  106. ).$input;
  107. this.letterhead_selector_df = this.add_sidebar_item(
  108. {
  109. fieldtype: 'Autocomplete',
  110. fieldname: 'letterhead',
  111. label: __('Select Letterhead'),
  112. placeholder: __('Select Letterhead'),
  113. options: [__('No Letterhead')],
  114. change: () => this.preview(),
  115. default: this.print_settings.with_letterhead
  116. ? __('No Letterhead')
  117. : __('Select Letterhead')
  118. },
  119. );
  120. this.letterhead_selector = this.letterhead_selector_df.$input;
  121. this.sidebar_dynamic_section = $(
  122. `<div class="dynamic-settings"></div>`
  123. ).appendTo(this.sidebar);
  124. }
  125. add_sidebar_item(df, is_dynamic) {
  126. if (df.fieldtype == 'Select') {
  127. df.input_class = 'btn btn-default btn-sm text-left';
  128. }
  129. let field = frappe.ui.form.make_control({
  130. df: df,
  131. parent: is_dynamic ? this.sidebar_dynamic_section : this.sidebar,
  132. render_input: 1,
  133. });
  134. if (df.default != null) {
  135. field.set_input(df.default);
  136. }
  137. return field;
  138. }
  139. get_default_option_for_select(value) {
  140. return {
  141. label: value,
  142. value: value,
  143. disabled: true
  144. };
  145. }
  146. setup_menu() {
  147. this.page.clear_menu();
  148. this.page.add_menu_item(__('Print Settings'), () => {
  149. frappe.set_route('Form', 'Print Settings');
  150. });
  151. if (this.print_settings.enable_raw_printing == '1') {
  152. this.page.add_menu_item(__('Raw Printing Setting'), () => {
  153. this.printer_setting_dialog();
  154. });
  155. }
  156. if (frappe.model.can_create('Print Format')) {
  157. this.page.add_menu_item(__('Customize'), () =>
  158. this.edit_print_format()
  159. );
  160. }
  161. if (cint(this.print_settings.enable_print_server)) {
  162. this.page.add_menu_item(__('Select Network Printer'), () =>
  163. this.network_printer_setting_dialog()
  164. );
  165. }
  166. }
  167. show(frm) {
  168. this.frm = frm;
  169. this.set_title();
  170. this.set_breadcrumbs();
  171. this.setup_customize_dialog();
  172. // print format builder beta
  173. this.page.add_inner_message(`
  174. <a style="line-height: 2.4" href="/app/print-format-builder-beta?doctype=${this.frm.doctype}">
  175. ${__('Try the new Print Format Builder')}
  176. </a>
  177. `);
  178. let tasks = [
  179. this.refresh_print_options,
  180. this.set_default_print_language,
  181. this.set_letterhead_options,
  182. this.preview,
  183. ].map((fn) => fn.bind(this));
  184. this.setup_additional_settings();
  185. return frappe.run_serially(tasks);
  186. }
  187. set_breadcrumbs() {
  188. frappe.breadcrumbs.add(this.frm.meta.module, this.frm.doctype);
  189. }
  190. setup_additional_settings() {
  191. this.additional_settings = {};
  192. this.sidebar_dynamic_section.empty();
  193. frappe
  194. .xcall('frappe.printing.page.print.print.get_print_settings_to_show', {
  195. doctype: this.frm.doc.doctype,
  196. docname: this.frm.doc.name
  197. })
  198. .then((settings) => this.add_settings_to_sidebar(settings));
  199. }
  200. add_settings_to_sidebar(settings) {
  201. for (let df of settings) {
  202. let field = this.add_sidebar_item({
  203. ...df,
  204. change: () => {
  205. const val = field.get_value();
  206. this.additional_settings[field.df.fieldname] = val;
  207. this.preview();
  208. },
  209. }, true);
  210. }
  211. }
  212. edit_print_format() {
  213. let print_format = this.get_print_format();
  214. let is_custom_format =
  215. print_format.name &&
  216. (print_format.print_format_builder || print_format.print_format_builder_beta) &&
  217. print_format.standard === 'No';
  218. let is_standard_but_editable =
  219. print_format.name && print_format.custom_format;
  220. if (is_standard_but_editable) {
  221. frappe.set_route('Form', 'Print Format', print_format.name);
  222. return;
  223. }
  224. if (is_custom_format) {
  225. if (print_format.print_format_builder_beta) {
  226. frappe.set_route('print-format-builder-beta', print_format.name);
  227. } else {
  228. frappe.set_route('print-format-builder', print_format.name);
  229. }
  230. return;
  231. }
  232. // start a new print format
  233. frappe.prompt(
  234. [
  235. {
  236. label: __('New Print Format Name'),
  237. fieldname: 'print_format_name',
  238. fieldtype: 'Data',
  239. reqd: 1,
  240. },
  241. {
  242. label: __('Based On'),
  243. fieldname: 'based_on',
  244. fieldtype: 'Read Only',
  245. default: print_format.name || 'Standard',
  246. },
  247. {
  248. label: __('Use the new Print Format Builder'),
  249. fieldname: 'beta',
  250. fieldtype: 'Check'
  251. },
  252. ],
  253. (data) => {
  254. frappe.route_options = {
  255. make_new: true,
  256. doctype: this.frm.doctype,
  257. name: data.print_format_name,
  258. based_on: data.based_on,
  259. beta: data.beta
  260. };
  261. frappe.set_route('print-format-builder');
  262. this.print_sel.val(data.print_format_name);
  263. },
  264. __('New Custom Print Format'),
  265. __('Start')
  266. );
  267. }
  268. refresh_print_format() {
  269. this.set_default_print_language();
  270. this.toggle_raw_printing();
  271. this.preview();
  272. }
  273. // bind_events () {
  274. // // // hide print view on pressing escape, only if there is no focus on any input
  275. // // $(document).on("keydown", function (e) {
  276. // // if (e.which === 27 && me.frm && e.target === document.body) {
  277. // // me.hide();
  278. // // }
  279. // // });
  280. // }
  281. setup_customize_dialog() {
  282. let print_format = this.get_print_format();
  283. $(document).on('new-print-format', (e) => {
  284. this.refresh_print_options();
  285. if (e.print_format) {
  286. this.print_sel.val(e.print_format);
  287. }
  288. // start a new print format
  289. frappe.prompt(
  290. [
  291. {
  292. label: __('New Print Format Name'),
  293. fieldname: 'print_format_name',
  294. fieldtype: 'Data',
  295. reqd: 1,
  296. },
  297. {
  298. label: __('Based On'),
  299. fieldname: 'based_on',
  300. fieldtype: 'Read Only',
  301. default: print_format.name || 'Standard',
  302. },
  303. ],
  304. (data) => {
  305. frappe.route_options = {
  306. make_new: true,
  307. doctype: this.frm.doctype,
  308. name: data.print_format_name,
  309. based_on: data.based_on,
  310. };
  311. frappe.set_route('print-format-builder');
  312. },
  313. __('New Custom Print Format'),
  314. __('Start')
  315. );
  316. });
  317. }
  318. setup_keyboard_shortcuts() {
  319. this.wrapper.find('.print-toolbar a.btn-default').each((i, el) => {
  320. frappe.ui.keys.get_shortcut_group(this.frm.page).add($(el));
  321. });
  322. }
  323. set_letterhead_options() {
  324. let letterhead_options = [__('No Letterhead')];
  325. let default_letterhead;
  326. let doc_letterhead = this.frm.doc.letter_head;
  327. return frappe.db
  328. .get_list('Letter Head', {
  329. filters: {'disabled': 0},
  330. fields: ['name', 'is_default'],
  331. limit: 0
  332. })
  333. .then((letterheads) => {
  334. letterheads.map((letterhead) => {
  335. if (letterhead.is_default) default_letterhead = letterhead.name;
  336. return letterhead_options.push(letterhead.name);
  337. });
  338. this.letterhead_selector_df.set_data(letterhead_options);
  339. let selected_letterhead = doc_letterhead || default_letterhead;
  340. if (selected_letterhead)
  341. this.letterhead_selector.val(selected_letterhead);
  342. });
  343. }
  344. set_user_lang() {
  345. this.lang_code = this.language_sel.val();
  346. }
  347. get_language_options() {
  348. return frappe.get_languages();
  349. }
  350. set_default_print_language() {
  351. let print_format = this.get_print_format();
  352. this.lang_code =
  353. print_format.default_print_language ||
  354. this.frm.doc.language ||
  355. frappe.boot.lang;
  356. this.language_sel.val(this.lang_code);
  357. }
  358. toggle_raw_printing() {
  359. const is_raw_printing = this.is_raw_printing();
  360. this.wrapper.find('.btn-print-preview').toggle(!is_raw_printing);
  361. this.wrapper.find('.btn-download-pdf').toggle(!is_raw_printing);
  362. }
  363. preview() {
  364. let print_format = this.get_print_format();
  365. if (print_format.print_format_builder_beta) {
  366. this.print_wrapper.find('.print-preview-wrapper').hide();
  367. this.print_wrapper.find('.preview-beta-wrapper').show();
  368. this.preview_beta();
  369. return;
  370. }
  371. this.print_wrapper.find('.preview-beta-wrapper').hide();
  372. this.print_wrapper.find('.print-preview-wrapper').show();
  373. const $print_format = this.print_wrapper.find('iframe');
  374. this.$print_format_body = $print_format.contents();
  375. this.get_print_html((out) => {
  376. if (!out.html) {
  377. out.html = this.get_no_preview_html();
  378. }
  379. this.setup_print_format_dom(out, $print_format);
  380. const print_height = $print_format.get(0).offsetHeight;
  381. const $message = this.wrapper.find('.page-break-message');
  382. const print_height_inches = frappe.dom.pixel_to_inches(print_height);
  383. // if contents are large enough, indicate that it will get printed on multiple pages
  384. // Maximum height for an A4 document is 11.69 inches
  385. if (print_height_inches > 11.69) {
  386. $message.text(__('This may get printed on multiple pages'));
  387. } else {
  388. $message.text('');
  389. }
  390. });
  391. }
  392. preview_beta() {
  393. let print_format = this.get_print_format();
  394. const iframe = this.print_wrapper.find('.preview-beta-wrapper iframe');
  395. let params = new URLSearchParams({
  396. doctype: this.frm.doc.doctype,
  397. name: this.frm.doc.name,
  398. print_format: print_format.name
  399. });
  400. let letterhead = this.get_letterhead();
  401. if (letterhead) {
  402. params.append("letterhead", letterhead);
  403. }
  404. iframe.prop('src', `/printpreview?${params.toString()}`);
  405. }
  406. setup_print_format_dom(out, $print_format) {
  407. this.print_wrapper.find('.print-format-skeleton').remove();
  408. let base_url = frappe.urllib.get_base_url();
  409. let print_css = frappe.assets.bundled_asset('print.bundle.css', frappe.utils.is_rtl(this.lang_code));
  410. this.$print_format_body.find('html').attr('dir', frappe.utils.is_rtl(this.lang_code) ? 'rtl': 'ltr');
  411. this.$print_format_body.find('html').attr('lang', this.lang_code);
  412. this.$print_format_body.find('head').html(
  413. `<style type="text/css">${out.style}</style>
  414. <link href="${base_url}${print_css}" rel="stylesheet">`
  415. );
  416. this.$print_format_body.find('body').html(
  417. `<div class="print-format print-format-preview">${out.html}</div>`
  418. );
  419. this.show_footer();
  420. this.$print_format_body.find('.print-format').css({
  421. display: 'flex',
  422. flexDirection: 'column',
  423. });
  424. this.$print_format_body.find('.page-break').css({
  425. display: 'flex',
  426. 'flex-direction': 'column',
  427. flex: '1',
  428. });
  429. setTimeout(() => {
  430. $print_format.height(this.$print_format_body.find('.print-format').outerHeight());
  431. }, 500);
  432. }
  433. hide() {
  434. if (this.frm.setup_done && this.frm.page.current_view_name === 'print') {
  435. this.frm.page.set_view(
  436. this.frm.page.previous_view_name === 'print'
  437. ? 'main'
  438. : this.frm.page.previous_view_name || 'main'
  439. );
  440. }
  441. }
  442. show_footer() {
  443. // footer is hidden by default as reqd by pdf generation
  444. // simple hack to show it in print preview
  445. this.$print_format_body.find('#footer-html').attr(
  446. 'style',
  447. `
  448. display: block !important;
  449. order: 1;
  450. margin-top: auto;
  451. padding-top: var(--padding-xl)
  452. `
  453. );
  454. }
  455. printit() {
  456. let me = this;
  457. if (cint(me.print_settings.enable_print_server)) {
  458. if (localStorage.getItem('network_printer')) {
  459. me.print_by_server();
  460. } else {
  461. me.network_printer_setting_dialog(() => me.print_by_server());
  462. }
  463. } else if (me.get_mapped_printer().length === 1) {
  464. // printer is already mapped in localstorage (applies for both raw and pdf )
  465. if (me.is_raw_printing()) {
  466. me.get_raw_commands(function(out) {
  467. frappe.ui.form
  468. .qz_connect()
  469. .then(function() {
  470. let printer_map = me.get_mapped_printer()[0];
  471. let data = [out.raw_commands];
  472. let config = qz.configs.create(printer_map.printer);
  473. return qz.print(config, data);
  474. })
  475. .then(frappe.ui.form.qz_success)
  476. .catch((err) => {
  477. frappe.ui.form.qz_fail(err);
  478. });
  479. });
  480. } else {
  481. frappe.show_alert(
  482. {
  483. message: __('PDF printing via "Raw Print" is not supported.'),
  484. subtitle: __(
  485. 'Please remove the printer mapping in Printer Settings and try again.'
  486. ),
  487. indicator: 'info',
  488. },
  489. 14
  490. );
  491. //Note: need to solve "Error: Cannot parse (FILE)<URL> as a PDF file" to enable qz pdf printing.
  492. }
  493. } else if (me.is_raw_printing()) {
  494. // printer not mapped in localstorage and the current print format is raw printing
  495. frappe.show_alert(
  496. {
  497. message: __('Printer mapping not set.'),
  498. subtitle: __(
  499. 'Please set a printer mapping for this print format in the Printer Settings'
  500. ),
  501. indicator: 'warning',
  502. },
  503. 14
  504. );
  505. me.printer_setting_dialog();
  506. } else {
  507. me.render_page('/printview?', true);
  508. }
  509. }
  510. print_by_server() {
  511. let me = this;
  512. if (localStorage.getItem('network_printer')) {
  513. frappe.call({
  514. method: 'frappe.utils.print_format.print_by_server',
  515. args: {
  516. doctype: me.frm.doc.doctype,
  517. name: me.frm.doc.name,
  518. printer_setting: localStorage.getItem('network_printer'),
  519. print_format: me.selected_format(),
  520. no_letterhead: me.with_letterhead(),
  521. letterhead: me.get_letterhead(),
  522. },
  523. callback: function() {},
  524. });
  525. }
  526. }
  527. network_printer_setting_dialog(callback) {
  528. frappe.call({
  529. method: 'frappe.printing.doctype.network_printer_settings.network_printer_settings.get_network_printer_settings',
  530. callback: function(r) {
  531. if (r.message) {
  532. let d = new frappe.ui.Dialog({
  533. title: __('Select Network Printer'),
  534. fields: [
  535. {
  536. "label": "Printer",
  537. "fieldname": "printer",
  538. "fieldtype": "Select",
  539. "reqd": 1,
  540. "options": r.message
  541. }
  542. ],
  543. primary_action: function() {
  544. localStorage.setItem('network_printer', d.get_values().printer);
  545. if (typeof callback == "function") {
  546. callback();
  547. }
  548. d.hide();
  549. },
  550. primary_action_label: __('Select')
  551. });
  552. d.show();
  553. }
  554. },
  555. });
  556. }
  557. render_pdf() {
  558. let print_format = this.get_print_format();
  559. if (print_format.print_format_builder_beta) {
  560. let params = new URLSearchParams({
  561. doctype: this.frm.doc.doctype,
  562. name: this.frm.doc.name,
  563. print_format: print_format.name,
  564. letterhead: this.get_letterhead()
  565. });
  566. let w = window.open(`/api/method/frappe.utils.weasyprint.download_pdf?${params}`);
  567. if (!w) {
  568. frappe.msgprint(__('Please enable pop-ups'));
  569. return;
  570. }
  571. } else {
  572. this.render_page('/api/method/frappe.utils.print_format.download_pdf?');
  573. }
  574. }
  575. render_page(method, printit = false) {
  576. let w = window.open(
  577. frappe.urllib.get_full_url(
  578. method +
  579. 'doctype=' +
  580. encodeURIComponent(this.frm.doc.doctype) +
  581. '&name=' +
  582. encodeURIComponent(this.frm.doc.name) +
  583. (printit ? '&trigger_print=1' : '') +
  584. '&format=' +
  585. encodeURIComponent(this.selected_format()) +
  586. '&no_letterhead=' +
  587. (this.with_letterhead() ? '0' : '1') +
  588. '&letterhead=' +
  589. encodeURIComponent(this.get_letterhead()) +
  590. '&settings=' +
  591. encodeURIComponent(JSON.stringify(this.additional_settings)) +
  592. (this.lang_code ? '&_lang=' + this.lang_code : '')
  593. )
  594. );
  595. if (!w) {
  596. frappe.msgprint(__('Please enable pop-ups'));
  597. return;
  598. }
  599. }
  600. get_print_html(callback) {
  601. let print_format = this.get_print_format();
  602. if (print_format.raw_printing) {
  603. callback({
  604. html: this.get_no_preview_html(),
  605. });
  606. return;
  607. }
  608. if (this._req) {
  609. this._req.abort();
  610. }
  611. this._req = frappe.call({
  612. method: 'frappe.www.printview.get_html_and_style',
  613. args: {
  614. doc: this.frm.doc,
  615. print_format: this.selected_format(),
  616. no_letterhead: !this.with_letterhead() ? 1 : 0,
  617. letterhead: this.get_letterhead(),
  618. settings: this.additional_settings,
  619. _lang: this.lang_code,
  620. },
  621. callback: function(r) {
  622. if (!r.exc) {
  623. callback(r.message);
  624. }
  625. },
  626. });
  627. }
  628. get_letterhead() {
  629. return this.letterhead_selector.val();
  630. }
  631. get_no_preview_html() {
  632. return `<div class="text-muted text-center" style="font-size: 1.2em;">
  633. ${__('No Preview Available')}
  634. </div>`;
  635. }
  636. get_raw_commands(callback) {
  637. // fetches rendered raw commands from the server for the current print format.
  638. frappe.call({
  639. method: 'frappe.www.printview.get_rendered_raw_commands',
  640. args: {
  641. doc: this.frm.doc,
  642. print_format: this.selected_format(),
  643. _lang: this.lang_code,
  644. },
  645. callback: function(r) {
  646. if (!r.exc) {
  647. callback(r.message);
  648. }
  649. },
  650. });
  651. }
  652. get_mapped_printer() {
  653. // returns a list of "print format: printer" mapping filtered by the current print format
  654. let print_format_printer_map = this.get_print_format_printer_map();
  655. if (print_format_printer_map[this.frm.doctype]) {
  656. return print_format_printer_map[this.frm.doctype].filter(
  657. (printer_map) => printer_map.print_format == this.selected_format()
  658. );
  659. } else {
  660. return [];
  661. }
  662. }
  663. get_print_format_printer_map() {
  664. // returns the whole object "print_format_printer_map" stored in the localStorage.
  665. try {
  666. let print_format_printer_map = JSON.parse(
  667. localStorage.print_format_printer_map
  668. );
  669. return print_format_printer_map;
  670. } catch (e) {
  671. return {};
  672. }
  673. }
  674. refresh_print_options() {
  675. this.print_formats = frappe.meta.get_print_formats(this.frm.doctype);
  676. const print_format_select_val = this.print_sel.val();
  677. this.print_sel.empty().add_options([
  678. this.get_default_option_for_select(__('Select Print Format')),
  679. ...this.print_formats
  680. ]);
  681. return this.print_formats.includes(print_format_select_val)
  682. && this.print_sel.val(print_format_select_val);
  683. }
  684. selected_format() {
  685. return (
  686. this.print_sel.val() || this.frm.meta.default_print_format || 'Standard'
  687. );
  688. }
  689. is_raw_printing(format) {
  690. return this.get_print_format(format).raw_printing === 1;
  691. }
  692. get_print_format(format) {
  693. let print_format = {};
  694. if (!format) {
  695. format = this.selected_format();
  696. }
  697. if (locals['Print Format'] && locals['Print Format'][format]) {
  698. print_format = locals['Print Format'][format];
  699. }
  700. return print_format;
  701. }
  702. with_letterhead() {
  703. return cint(this.get_letterhead() !== __('No Letterhead'));
  704. }
  705. set_style(style) {
  706. frappe.dom.set_style(style || frappe.boot.print_css, 'print-style');
  707. }
  708. printer_setting_dialog() {
  709. // dialog for the Printer Settings
  710. this.print_format_printer_map = this.get_print_format_printer_map();
  711. this.data = this.print_format_printer_map[this.frm.doctype] || [];
  712. this.printer_list = [];
  713. frappe.ui.form.qz_get_printer_list().then((data) => {
  714. this.printer_list = data;
  715. const dialog = new frappe.ui.Dialog({
  716. title: __('Printer Settings'),
  717. fields: [
  718. {
  719. fieldtype: 'Section Break',
  720. },
  721. {
  722. fieldname: 'printer_mapping',
  723. fieldtype: 'Table',
  724. label: __('Printer Mapping'),
  725. in_place_edit: true,
  726. data: this.data,
  727. get_data: () => {
  728. return this.data;
  729. },
  730. fields: [
  731. {
  732. fieldtype: 'Select',
  733. fieldname: 'print_format',
  734. default: 0,
  735. options: this.print_formats,
  736. read_only: 0,
  737. in_list_view: 1,
  738. label: __('Print Format'),
  739. },
  740. {
  741. fieldtype: 'Select',
  742. fieldname: 'printer',
  743. default: 0,
  744. options: this.printer_list,
  745. read_only: 0,
  746. in_list_view: 1,
  747. label: __('Printer'),
  748. },
  749. ],
  750. },
  751. ],
  752. primary_action: () => {
  753. let printer_mapping = dialog.get_values()['printer_mapping'];
  754. if (printer_mapping && printer_mapping.length) {
  755. let print_format_list = printer_mapping.map((a) => a.print_format);
  756. let has_duplicate = print_format_list.some(
  757. (item, idx) => print_format_list.indexOf(item) != idx
  758. );
  759. if (has_duplicate)
  760. frappe.throw(
  761. __(
  762. 'Cannot have multiple printers mapped to a single print format.'
  763. )
  764. );
  765. } else {
  766. printer_mapping = [];
  767. }
  768. dialog.print_format_printer_map = this.get_print_format_printer_map();
  769. dialog.print_format_printer_map[this.frm.doctype] = printer_mapping;
  770. localStorage.print_format_printer_map = JSON.stringify(
  771. dialog.print_format_printer_map
  772. );
  773. dialog.hide();
  774. },
  775. primary_action_label: __('Save'),
  776. });
  777. dialog.show();
  778. if (!(this.printer_list && this.printer_list.length)) {
  779. frappe.throw(__('No Printer is Available.'));
  780. }
  781. });
  782. }
  783. };