Quellcode durchsuchen

feat(Raw Printing): Adding Support for qz-tray

version-14
karthikeyan5 vor 6 Jahren
Ursprung
Commit
70c256f81b
7 geänderte Dateien mit 1113 neuen und 722 gelöschten Zeilen
  1. +826
    -712
      frappe/printing/doctype/print_format/print_format.json
  2. +3
    -1
      frappe/public/build.json
  3. +234
    -3
      frappe/public/js/frappe/form/print.js
  4. +3
    -1
      frappe/public/js/frappe/form/templates/print_layout.html
  5. +35
    -5
      frappe/www/printview.py
  6. +2
    -0
      package.json
  7. +10
    -0
      yarn.lock

+ 826
- 712
frappe/printing/doctype/print_format/print_format.json
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 3
- 1
frappe/public/build.json Datei anzeigen

@@ -153,7 +153,9 @@
"public/js/lib/leaflet/leaflet.js",
"public/js/lib/leaflet/leaflet.draw.js",
"public/js/lib/leaflet/L.Control.Locate.js",
"public/js/lib/leaflet/easy-button.js"
"public/js/lib/leaflet/easy-button.js",
"node_modules/js-sha256/build/sha256.min.js",
"node_modules/qz-tray/qz-tray.js"
],
"js/desk.min.js": [
"public/js/frappe/class.js",


+ 234
- 3
frappe/public/js/frappe/form/print.js Datei anzeigen

@@ -1,5 +1,9 @@
frappe.provide("frappe.ui.form");

// init qz tray library
qz.api.setPromiseType(function promise(resolver) { return new Promise(resolver); });
qz.api.setSha256Type(function(data) { return sha256(data); });

frappe.ui.form.PrintPreview = Class.extend({
init: function (opts) {
$.extend(this, opts);
@@ -47,6 +51,10 @@ frappe.ui.form.PrintPreview = Class.extend({
me.multilingual_preview()
});

this.wrapper.find(".btn-qz-settings").click(function () {
me.qz_setting_dialog()
});

this.wrapper.find(".btn-print-print").click(function () {
if (me.is_old_style()) {
me.print_old_style();
@@ -125,10 +133,18 @@ frappe.ui.form.PrintPreview = Class.extend({
multilingual_preview: function () {
var me = this;
if (this.is_old_style()) {
me.wrapper.find(".btn-print-preview").toggle(true);
me.wrapper.find(".btn-download-pdf").toggle(false);
me.set_style();
me.preview_old_style();
} else {
me.preview_old_style();
}
else if (this.is_raw_printing()){
me.wrapper.find(".btn-print-preview").toggle(false);
me.wrapper.find(".btn-download-pdf").toggle(false);
me.preview();
}
else {
me.wrapper.find(".btn-print-preview").toggle(true);
me.wrapper.find(".btn-download-pdf").toggle(true);
me.preview();
}
@@ -190,7 +206,47 @@ frappe.ui.form.PrintPreview = Class.extend({
callback: function (data) {
}
});
} else {
}
else if(me.get_mapped_printer().length == 1){
// printer is already mapped in localstorage (applies for both raw and pdf )
if(me.is_raw_printing()){
me.get_raw_commands(function(out) {
let printer_map = me.get_mapped_printer()[0]
let config = qz.configs.create(printer_map.printer)
let data = [out.raw_commands]
frappe.ui.form.qz_connect().then(function(){
return qz.print(config,data);
}).then(frappe.ui.form.qz_success).catch((err)=>{
frappe.ui.form.qz_fail(err);
})
})
}
else{
frappe.show_alert({message:__('PDF Printing via QZ is not yet supported. Please remove QZ printer mapping for this Print format and try again.'),indicator:'blue'},14)
//Note: need to solve "Error: Cannot parse (FILE)<URL> as a PDF file" to enable qz pdf printing.

// // use pdf method print method of qz
// let printer_map = me.get_mapped_printer()[0]
// let config = qz.configs.create(printer_map.printer)
// let pdf_url = frappe.urllib.get_full_url("/api/method/frappe.utils.print_format.download_pdf?"
// + "doctype=" + encodeURIComponent(me.frm.doc.doctype)
// + "&name=" + encodeURIComponent(me.frm.doc.name)
// + "&format=" + me.selected_format()
// + "&no_letterhead=" + (me.with_letterhead() ? "0" : "1")
// + (me.lang_code ? ("&_lang=" + me.lang_code) : ""))
// let data = [{type: 'pdf', data: pdf_url}]
// frappe.ui.form.qz_connect().then(function(){
// return qz.print(config,data);
// }).then(frappe.ui.form.qz_success).catch((err)=>{
// frappe.ui.form.qz_fail(err);
// })
}
}
else if(me.is_raw_printing()) {
frappe.show_alert({message:__('Please set a printer mapping for this print format in the QZ Settings'),indicator:'blue'},14)
me.qz_setting_dialog()
}
else {
me.new_page_preview(true);
}
}
@@ -225,6 +281,31 @@ frappe.ui.form.PrintPreview = Class.extend({
}
});
},
get_raw_commands: function (callback) {
frappe.call({
method: "frappe.www.printview.get_rendered_raw_commands",
args: {
doc: this.frm.doc,
print_format: this.selected_format(),
_lang: this.lang_code
},
callback: function (r) {
if (!r.exc) {
callback(r.message);
}
}
});
},
get_mapped_printer: function() {
if(localStorage && localStorage.print_format_printer_map
&& JSON.parse(localStorage.print_format_printer_map)[this.frm.doctype]) {
return (JSON.parse(localStorage.print_format_printer_map)[this.frm.doctype])
.filter((printer_map)=> printer_map.print_format == this.selected_format())
}
else {
return []
}
},
preview_old_style: function () {
var me = this;
this.with_old_style({
@@ -270,6 +351,9 @@ frappe.ui.form.PrintPreview = Class.extend({
is_old_style: function (format) {
return this.get_print_format(format).print_format_type === "Client";
},
is_raw_printing: function (format) {
return this.get_print_format(format).raw_printing == true;
},
get_print_format: function (format) {
if (!format) {
format = this.selected_format();
@@ -286,6 +370,76 @@ frappe.ui.form.PrintPreview = Class.extend({
},
set_style: function (style) {
frappe.dom.set_style(style || frappe.boot.print_css, "print-style");
},
qz_setting_dialog: function() {
var me = this
if (localStorage && localStorage.print_format_printer_map)
this.print_format_printer_map = JSON.parse(localStorage.print_format_printer_map);
else
this.print_format_printer_map = {}
this.data = [];
this.data = this.print_format_printer_map[this.frm.doctype] || []
this.printer_list = [];
frappe.ui.form.qz_get_printer_list().then((data)=>{
this.printer_list = data;
if (!(this.printer_list && this.printer_list.length)) {
frappe.throw(__("No Printer is Available."));
}
const dialog = new frappe.ui.Dialog({
title: __("QZ Tray Print Settings"),
fields: [
{fieldtype:'Section Break', label: __('Printer Mapping')},
{
fieldname: "printer_mapping",
fieldtype: "Table",
in_place_edit: true,
data: this.data,
get_data: () => {
return this.data;
},
fields: [{
fieldtype:'Select',
fieldname:"print_format",
default: 0,
options: this.print_formats,
read_only: 0,
in_list_view: 1,
label: __('Print Format')
}, {
fieldtype:'Select',
fieldname:"printer",
default: 0,
options: this.printer_list,
read_only: 0,
in_list_view: 1,
label: __('Printer')
}]
},
],
primary_action: function() {
let printer_mapping = this.get_values()["printer_mapping"];
if (printer_mapping && printer_mapping.length) {
let print_format_list = printer_mapping.map(a => a.print_format);
let has_duplicate = print_format_list.some((item, idx) => print_format_list.indexOf(item) != idx )
if (has_duplicate)
frappe.throw(__("Cannot have multiple printers mapped to a single print format."));
}
else {
printer_mapping = []
}
if (localStorage && localStorage.print_format_printer_map)
this.print_format_printer_map = JSON.parse(localStorage.print_format_printer_map);
else
this.print_format_printer_map = {}
this.print_format_printer_map[me.frm.doctype] = printer_mapping;
localStorage.print_format_printer_map = JSON.stringify(this.print_format_printer_map)
this.hide();
},
primary_action_label: __('Save')
});
dialog.show();
});
}
});

@@ -326,3 +480,80 @@ frappe.ui.get_print_settings = function (pdf, callback, letter_head) {
callback(data);
}, __("Print Settings"));
}


// qz connection wrapper
// - allows active and inactive connections to resolve regardless
// - try to connect once before firing the mimetype launcher
// - if connection fails, catch the reject, fire the mimetype launcher
// - after mimetype launcher is fired, try to connect 3 more times
// - display success/fail meaasges to user
frappe.ui.form.qz_connect = function() {
return new Promise(function(resolve, reject) {
if (qz.websocket.isActive()) { // if already active, resolve immediately
// frappe.show_alert({message: __('QZ Tray Connection Active!'), indicator: 'green'});
resolve();
} else {
// try to connect once before firing the mimetype launcher
frappe.show_alert({message: __('Attemting Connection to QZ Tray!'), indicator: 'blue'});
qz.websocket.connect().then(()=>{
frappe.show_alert({message: __('Connected to QZ Tray!'), indicator: 'green'});
resolve();
}, function retry(err) {
if (err.message === 'Unable to establish connection with QZ'){
// if a connect was not succesful, launch the mimetime, try 3 more times
frappe.show_alert({message: __('Attemting to launch QZ Tray!'), indicator: 'blue'},14);
window.location.assign("qz:launch");
qz.websocket.connect({ retries: 3, delay: 1 }).then(()=>{
frappe.show_alert({message: __('Connected to QZ Tray!'), indicator: 'green'});
resolve();
}, (err)=>{
frappe.show_alert({message: __('Error connecting to QZ Tray! <a href="https://qz.io/download/">Click here to Download QZ Tray</a>'), indicator: 'red'},14);
reject();
});
}
else{
frappe.show_alert({message: 'QZ Tray '+err.toString(), indicator: 'red'},14);
reject();
}
});
}
});
}

frappe.ui.form.qz_get_printer_list = function(){
return frappe.ui.form.qz_connect().then(function(){
return qz.printers.find()
}).then((data)=>{
return data
}).catch((err)=>{
frappe.ui.form.qz_fail(err);
})
}

// notify qz successful print
frappe.ui.form.qz_success = function() {
frappe.show_alert({message: __('QZ print complete!'), indicator: 'green'});
}

// notify qz errors
frappe.ui.form.qz_fail = function(e) {
console.error("qz error:",e)
frappe.show_alert({message:__("QZ Tray Failed") + e.toString(), indicator:'red'},20);
}


// flow for action after print button is clicked
// - if printer is already mapped in localstorage (applies for both raw and pdf )
// - qz_connect()
// - search for configured printer and create config
// - if the above fails throw error with printer not found. (and instructions/options to remove all mapping from localstorage for this printer)
// - if raw_printing call appropriate qz fn
// - else call pdf and then call appropriate qz fn with that pdf
// - else if raw_printing == true
// - qz_connect()
// - if search returns printers
// - show modal with list of printer and ask to map
// - store in LocalStorage
// - else throw error that no printer is available


+ 3
- 1
frappe/public/js/frappe/form/templates/print_layout.html Datei anzeigen

@@ -18,7 +18,9 @@
<a class="btn-print-print btn-sm btn btn-default">
<strong>{%= __("Print") %}</strong></a>
<a class="btn-sm btn btn-default" href="#Form/Print Settings">
{%= __("Settings...") %}</a>
{%= __("Settings...") %}</a>
<a class="btn-qz-settings btn-sm btn btn-default">
{%= __("QZ Settings...") %}</a>
<a class="btn-print-edit btn-sm btn btn-default">
{%= __("Customize...") %}</a>
<a class="btn-print-preview btn-sm btn btn-default">


+ 35
- 5
frappe/www/printview.py Datei anzeigen

@@ -34,7 +34,7 @@ def get_context(context):
print_format = get_print_format_doc(None, meta = meta)

return {
"body": get_html(doc, print_format = print_format,
"body": get_rendered_template(doc, print_format = print_format,
meta=meta, trigger_print = frappe.form_dict.trigger_print,
no_letterhead=frappe.form_dict.no_letterhead),
"css": get_print_style(frappe.form_dict.style, print_format),
@@ -58,7 +58,7 @@ def get_print_format_doc(print_format_name, meta):
# if old name, return standard!
return None

def get_html(doc, name=None, print_format=None, meta=None,
def get_rendered_template(doc, name=None, print_format=None, meta=None,
no_letterhead=None, trigger_print=False):

print_settings = frappe.db.get_singles_dict("Print Settings")
@@ -179,14 +179,42 @@ def get_html_and_style(doc, name=None, print_format=None, meta=None,

if isinstance(doc, string_types):
doc = frappe.get_doc(json.loads(doc))
print_format = get_print_format_doc(print_format, meta=meta or frappe.get_meta(doc.doctype))

if print_format and print_format.raw_printing:
return {
"html": '<div class="alert alert-info">'
+ _("Note: This Print Format is in Raw Commands and cannot be previewed.")
+ '</div>'
}

return {
"html": get_html(doc, name=name, print_format=print_format, meta=meta,
"html": get_rendered_template(doc, name=name, print_format=print_format, meta=meta,
no_letterhead=no_letterhead, trigger_print=trigger_print),
"style": get_print_style(style=style, print_format=print_format)
}

@frappe.whitelist()
def get_rendered_raw_commands(doc, name=None, print_format=None, meta=None, lang=None):
"""Returns Rendered Raw Commands of print format, used to send directly to printer"""

if isinstance(doc, string_types) and isinstance(name, string_types):
doc = frappe.get_doc(doc, name)

if isinstance(doc, string_types):
doc = frappe.get_doc(json.loads(doc))
print_format = get_print_format_doc(print_format, meta=meta or frappe.get_meta(doc.doctype))

if not print_format or (print_format and not print_format.raw_printing):
frappe.throw(_("{0} is not a raw printing format.").format(print_format),
frappe.TemplateNotFoundError)

return {
"raw_commands": get_rendered_template(doc, name=name, print_format=print_format, meta=meta)
}

def validate_print_permission(doc):
if frappe.form_dict.get("key"):
if frappe.form_dict.key == doc.get_signature():
@@ -218,8 +246,10 @@ def get_print_format(doctype, print_format):
with open(path, "r") as pffile:
return pffile.read()
else:
if print_format.html:
if print_format.html and not print_format.raw_printing:
return print_format.html
elif print_format.raw_commands and print_format.raw_printing:
return print_format.raw_commands
else:
frappe.throw(_("No template found at path: {0}").format(path),
frappe.TemplateNotFoundError)


+ 2
- 0
package.json Datei anzeigen

@@ -28,10 +28,12 @@
"frappe-gantt": "^0.1.0",
"fuse.js": "^3.2.0",
"highlight.js": "^9.12.0",
"js-sha256": "^0.9.0",
"jsbarcode": "^3.9.0",
"moment": "^2.20.1",
"moment-timezone": "^0.5.21",
"quill": "2.0.0-dev.2",
"qz-tray": "^2.0.8",
"redis": "^2.8.0",
"showdown": "^1.8.6",
"socket.io": "^2.0.4",


+ 10
- 0
yarn.lock Datei anzeigen

@@ -2452,6 +2452,11 @@ js-base64@^2.1.8, js-base64@^2.1.9:
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.1.tgz#1efa39ef2c5f7980bb1784ade4a8af2de3291121"
integrity sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==

js-sha256@^0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966"
integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==

js-yaml@^3.12.0, js-yaml@^3.9.0:
version "3.12.2"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.2.tgz#ef1d067c5a9d9cb65bd72f285b5d8105c77f14fc"
@@ -3852,6 +3857,11 @@ quill@2.0.0-dev.2:
parchment quilljs/parchment#487850f7eb030a6c4e750ba809e58b09444e0bdb
quill-delta "^3.6.2"

qz-tray@^2.0.8:
version "2.0.8"
resolved "https://registry.yarnpkg.com/qz-tray/-/qz-tray-2.0.8.tgz#5e15d102cf3a11a31ddb332891c7f8a6af8f6ad5"
integrity sha512-lncGYzz7/sTORZuC1S3ukNlMPCMOmsHWNvJF4FjMCZ2+0UV3txi6kgPd754B7kDFKm0J587sIODgxIlFY7qU4w==

ramda@0.24.1:
version "0.24.1"
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.24.1.tgz#c3b7755197f35b8dc3502228262c4c91ddb6b857"


Laden…
Abbrechen
Speichern