Files
webapp/assets/js/print.js
T

269 lines
7.6 KiB
JavaScript

/*******************************************************************
Module : /assets/js/print.js
Desc. : JS Class Printer & Printer POS
Created By : Siau Pheng ([email protected]).
Created Date : May 27th, 2007.
Last Modified : November 2nd, 2026.
(c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************
*
* Class Printer
*
* Standard Web Document Printing & Report Preview Class
*
* @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <[email protected]>
*
* @param {object} options
* @param {string} options.url - Endpoint URL Print Page
* @param {number} [options.zoom=90] - Zoom percentage in viewer
* @param {Array} [options.params] - Request payload
*/
class Printer {
constructor(options = {}) {
this.url = typeof options === 'string' ? options : options.url || '';
this.zoom = options.zoom || 90;
this.params = options.params || [];
}
addParam(id, val) {
this.params.push(val !== undefined ? `${id}=${encodeURIComponent(val)}` : id);
return this;
}
doPrint() {
if (!this.url) {
console.warn('Url Laporan dibutuhkan!');
return;
}
$.ajax({
type: 'POST',
url: this.url,
data: FoniaJS.RequestParam(this.params),
success: (txt) => {
let $printData = $('#dataPrint');
if (!$printData.length) {
$printData = $('<div/>', {
id: 'dataPrint',
class: 'd-none d-print-block print-body'
}).appendTo('body');
}
$printData.html(txt);
window.print();
},
error: typeof this.onFailure === 'function' ? this.onFailure : null
});
return this;
}
doPreview() {
if (!this.url) {
console.warn('Url Laporan dibutuhkan!');
return;
}
$.ajax({
type: 'POST',
url: this.url,
data: FoniaJS.RequestParam(this.params),
success: (txt) => {
if (!txt) {
FoniaJS.showInfo('Tidak ada data untuk pembuatan laporan !');
return false;
}
const modalHtml = `
<div class="modal fade" id="winAPPPrint" tabindex="-1" role="dialog" data-bs-keyboard="false" data-bs-show="false" data-bs-backdrop="static" aria-labelledby="winAPPPrintLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable modal-fs" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="winAPPPrintLabel">Preview Laporan</h5>
<div class="ms-auto">
<button class="btn me-2" id="tombCetak" type="button" aria-label="Cetak">
<i class="bi-printer me-2"></i><span aria-hidden="true">Cetak</span>
</button>
<button class="btn" type="button" data-bs-dismiss="modal" aria-label="Tutup">
<i class="bi-x-lg me-2"></i><span aria-hidden="true">Tutup</span>
</button>
</div>
</div>
<div class="modal-body print-body" id="divPrintP">${txt}</div>
</div>
</div>
</div>
`;
$('#winAPPPrint').modal('dispose').remove();
$(modalHtml)
.on('shown.bs.modal', () => {
$('#winAPPPrint #tombCetak').off('click').on('click', () => {
let $printData = $('#dataPrint');
if (!$printData.length) {
$printData = $('<div/>', {
id: 'dataPrint',
class: 'd-none d-print-block print-body'
}).appendTo('body');
}
$printData.html($('#divPrintP').html());
FoniaJS.closeWindow($('#winAPPPrint'));
window.print();
});
})
.on('hidden.bs.modal', function () {
$(this).remove();
})
.modal('show');
},
error: typeof this.onFailure === 'function' ? this.onFailure : null
});
return this;
}
}
/**
* Class PrinterPos
*
* Direct Thermal POS Printer Class (via QZ Tray API)
*
* @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <[email protected]>
*
* @param {object} options
* @param {string} options.url - Endpoint URL Print Page
* @param {number} [options.printer=FoniaJS._def_printer] - Printer Name
* @param {Array} [options.params] - Request payload
*/
class PrinterPos {
constructor(options = {}) {
this.url = typeof options === 'string' ? options : options.url || '';
this.printer = options.printer || (FoniaJS._def_printer ?? null);
this.cut_paper = options.cut_paper || '\x1D\x56\x41';
this.open_draw = options.open_draw || '\x1B\x70\x30\x19\x19';
this.chr_norm = options.chr_norm || '\x1B\x21\x01';
this.chr_bold = options.chr_bold || '\x1B\x21\x10';
this.chr_sbold = options.chr_sbold || '\x1B\x21\x31';
this.chr_uline = options.chr_uline || '\x1B\x21\x80';
this.params = options.params || [];
}
_detectPrinter() {
if (!qz?.websocket?.isActive?.()) {
FoniaJS.showInfo('Status Printer belum Active !');
return false;
}
return true;
}
setPrinter(nprint) {
if (nprint != null) this.printer = nprint;
return this;
}
addParam(id, val) {
this.params.push(val !== undefined ? `${id}=${encodeURIComponent(val)}` : id);
return this;
}
async exec() {
if (!this._detectPrinter()) return;
let printerName = this.printer;
if (typeof printerName === 'object' && printerName !== null) {
if (printerName.name !== undefined) {
printerName = printerName.name;
} else if (printerName.file !== undefined) {
printerName = printerName.file;
} else if (printerName.host !== undefined) {
printerName = `${printerName.host}:${printerName.port}`;
}
}
printerName = printerName ?? 'None';
this.params.push(`Printer=${printerName}`);
this.doPrint(this.printer, this.url, this.params);
}
doPrint(printerName, url, params) {
const qzcfg = qz.configs.create(printerName);
new Request({
url,
sendParam: params,
onSuccess: (ajson) => {
if (typeof ajson === 'object' && ajson !== null) {
const printPayload = ajson.type === 'escp' ? this.genEscp(ajson) : this.genEpl(ajson);
qz.print(qzcfg, printPayload);
} else {
FoniaJS.showInfo('Error dalam proses Cetak.');
}
}
}).send();
}
genEscp(adata) {
const ohead = ['\x1B\x40', '\x1B\x61\x01'];
if (adata.config?.logo) {
ohead.push({
type: 'raw',
format: 'image',
flavor: 'base64',
data: adata.config.logo,
options: { language: 'ESCPOS', dotDensity: 'double' }
});
ohead.push('\x1B\x32');
}
if (parseInt(adata.config?.fontsize, 10) === 2) {
this.chr_norm = '\x1B\x21\x02';
}
if (Array.isArray(adata.header)) {
adata.header.forEach((headerLine, index) => {
if (index === 0 && !adata.config?.logo) ohead.push(this.chr_bold);
ohead.push(`${headerLine}\x0A`);
if (index === 0 && !adata.config?.logo) ohead.push(this.chr_norm);
});
}
let ocetak = [];
if (Array.isArray(adata.content)) {
adata.content.forEach((sheetContent) => {
if (!sheetContent?.length) return;
const osheet = [...ohead];
sheetContent.forEach((line) => {
const formattedLine = line
.replace('_SBOLD_', this.chr_sbold)
.replace('_BOLD_', this.chr_bold)
.replace('_NORM_', this.chr_norm)
.replace('_ULINE_', this.chr_uline);
osheet.push(`${formattedLine}\x0A`);
});
if (adata.config?.cutpaper) {
osheet.push(`${this.cut_paper}\x0A`);
}
ocetak = ocetak.concat(osheet);
});
}
let notaout = [];
if (adata.config?.opendraw) {
notaout.push(this.open_draw);
}
const times = adata.config?.times || 1;
for (let i = 0; i < times; i++) {
notaout = notaout.concat(ocetak);
}
return notaout;
}
genEpl(adata) {
const cetak = (adata.content || []).map((line) => `${line}\r\n`);
let notaout = [];
const times = adata.config?.times || 1;
for (let i = 0; i < times; i++) {
notaout = notaout.concat(cetak);
}
return notaout;
}
}