0

A few days ago I was struggling to get to print receipts using ESCPOS Java API but failed since I couldn't find a simple enough USB API to allow the program to print to my thermal printer using a USB interface. I later decided to implement this in Node and ESCPOS module.

I am connecting this application to MySQL database to be able to print receipts from recorded transactions. I am using tables to create a list of products and their respective prices but only by hard coding. My problem now is how to create these tables dynamically. Depending on which products were involved in the transaction, I want the script to query and print only those.

Here is receipt code!

const db = require('./database.js');
const escpos = require('escpos');
const uniqueRandom = require('unique-random');
const random = uniqueRandom(1000, 9999);


const device  = new escpos.USB();

const options = { encoding: "GB18030" }

exports.printGeneralReceipt = function(status){
  db.getTransactionDetails(function(res){
    data = res;
  }); 

  const printer = new escpos.Printer(device, options);
  console.log("Printer found!");

  device.open(function(){
    console.log("Receipt generating...");
    printer
    .font('b')
    .align('ct')
    .style('bu')
    .size(1, 1)
    .encode('utf8')
    .text('\n*****START OF LEGAL RECEIPT*****'+
    '\n\nCOMPUTICKET MALAWI\n'+
    'SHOP 31A, GAME COMPLEX\n'+
    'LILONGWE MALL\n\nwww.computicket.mw\n+265 (0) 99 974 7576\n')
    .table(["BUYER NAME :", "CLIFFORD MWALE", ""])
    .table(["RECEIPT # :", random(), ""])
    .table(["DATE: ", "12/AUG/2019", ""])
    .text("----------ITEM LIST----------\n")

    // ITEM LIST STARTS HERE
    .table(["MILK","$2"])
    .table(["PEANUT BUTTER", "$6"])
    // ITEM LIST ENDS HERE

    .text("--------------------------------")
    .table(["TOTAL PRICE", "$8.00", ""])
    .text("Operator: Jon Doe\n-------------------------------\n")
    .barcode('123456789012')
    .text("\n\nTHANK YOU\n\n*****END OF LEGAL RECEIPT*****")
    .beep(1,100)
  .cut().close();
  console.log("Receipt printed!");
});

}

And here is the function pulling the transaction details from the Database. I'll spare you the overhead of creating a connection.


exports.getTransactionDetails = function(trans_id){
    var res = "";
    conn.query("SELECT * FROM transactions JOIN products_in_transaction WHERE transactions.trans_id = products_in_transaction.trans_id "+
        " transactions.trans_id = '"+trans_id+"'", 
        function (error, results, fields) {
       for(var i = 0; i < results.length; i++){
         // SOME OPERATION HERE
       }
      });
}

CliffTheCoder
  • 394
  • 1
  • 4
  • 24

2 Answers2

0

I will post some code I played with, and it worked;you need to install html-to-text

const escpos = require('escpos');

    // Select the adapter based on your printer type
    const device  = new escpos.USB();

    const printer = new escpos.Printer(device);


    const cartItems = [
        {category: 'test', price: 80, quantityToSell: 2, title: 'Hosting'},
        {category: 'test1', price: 820, quantityToSell: 63, title: 'Mouse'},
        {category: 'test00', price: 60, quantityToSell: 20, title: 'Sale'},
        {category: 'dvhfgnfgjfjg', price: 20, quantityToSell: 8, title: 'Keyboards'},
        {category: 'dvhfgnfgjfjg', price: 10, quantityToSell: 4, title: 'Keyss'},
        {category: 'dvhfgnfgjfjg', price: 70, quantityToSell: 1, title: 'Test'},
        {category: 'dvhfgnfgjfjg', price: 500, quantityToSell: 12, title: 'Whale oil'},
        {category: 'dvhfgnfgjfjg', price: 560, quantityToSell: 22, title: 'Papers'},
    ]

    // get total per line items
    const totalPerItemList = (item) => {

        let totalPerItem = 0

        totalPerItem = item.quantityToSell * item.price


        return totalPerItem
    }

    // get the total price
    let total = 0;
    for (let cartItem of cartItems) {
        var unitSum  = cartItem.quantityToSell * cartItem.price
        total += unitSum

    }

    // Create our html template, could be an html file on it's own
    const TestTable = `
    <!doctype html>
    <html>
        <head>
            <meta charset="utf-8">
            <title>Testing Title for table</title>            
        </head>   
        <body>
            <div class="invoice-box">
                <table class="receipt-table" cellpadding="0" cellspacing="0" border="0">
                    <thead>
                        <tr class="heading">
                            <th>Item</th>
                            <th>Quantity</th>
                            <th>Unit Price</th>
                            <th>Total</th>
                        </tr>
                    </thead>
                    <tbody>
                        ${cartItems.map(item =>
                            `
                                <tr>
                                    <td>${item.title}</td>
                                    <td>${item.quantityToSell}</td>
                                    <td>${item.price}</td>
                                    <td>${totalPerItemList(item)}</td>
                                </tr>
                            `
                        )}
                    </tbody>
                    <tfoot>
                        <tr>
                            <td>
                                TOTAL:${total}
                            </td>
                        </tr>                                                   
                    </tfoot>
            </table>
            </div>            
        </body>     
    </html>
    `


    const htmlToText = require('html-to-text');

    const text = htmlToText.fromString(TestTable, {
        wordwrap: false,
        tables: ['.receipt-box', '.receipt-table']
    });


    device.open(function(err){

        printer
            .font('a')
            .align('ct')
            .style('bu')
            .size(1, 1)
            .text('Printing Tables Dynamically with epos')
            .text('||||||||||||||||||||||||||')
            .text(text)
            .text('||||||||||||||||||||||||')
            .text('========================')
            .cut()
            .close()
    });
Nicholas
  • 77
  • 10
0

the fastest way to do this ive found is to do what ive done in my own project as in

'use strict'

import { app, protocol, BrowserWindow,ipcMain } from 'electron'
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'
const escpos = require('escpos');
escpos.USB = require('escpos-usb');
const isDevelopment = process.env.NODE_ENV !== 'production' 

// Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([
  { scheme: 'app', privileges: { secure: true, standard: true } }
])
async function createWindow() {
  // Create the browser window.
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      // Use pluginOptions.nodeIntegration, leave this alone
      // See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info
      nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION
    }
  })
  win.menuBarVisible = false;
  if (process.env.WEBPACK_DEV_SERVER_URL) {
    // Load the url of the dev server if in development mode
    await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
    if (!process.env.IS_TEST) win.webContents.openDevTools()
  } else {
    createProtocol('app')
    // Load the index.html when not in development
    win.loadURL('app://./index.html')
  }
}

// Quit when all windows are closed.
app.on('window-all-closed', () => {
  // On macOS it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', () => {
  // On macOS it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (BrowserWindow.getAllWindows().length === 0) createWindow()
})

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', async () => {
  if (isDevelopment && !process.env.IS_TEST) {
    // Install Vue Devtools
    try {
      await installExtension(VUEJS_DEVTOOLS)
    } catch (e) {
      console.error('Vue Devtools failed to install:', e.toString())
    }
  }
  createWindow()
})

// Exit cleanly on request from parent process in development mode.
if (isDevelopment) {
  if (process.platform === 'win32') {
    process.on('message', (data) => {
      if (data === 'graceful-exit') {
        app.quit()
      }
    })
  } else {
    process.on('SIGTERM', () => {
      app.quit()
    })
  }
}
function print(load){
  var _message = '';
 device.open(function(){
       _message = 'done'
        printer
        .font('a')
        .align('ct')
        .style('bu')
        .size(0.05, 0.05)
        .text('Road House Magodo')
        .text('Magodo Shopping Arcade Ayodele \r\n Fanoki Magodo Phase I')
        .table(['item','qty','total'])
        load.load.forEach((element)=>{
          return printer.table(element)
        })
        printer.text(`Total:   ${load.total}`)
        if(load.change!='card'){
          printer.text(`Change:   ${load.change}`)
        }else{
          printer.text('Method: Card')
        }
        printer.newLine()
        printer.newLine()
        printer.cut()

        printer
        .font('a')
        .align('ct')
        .style('bu')
        .size(0.05, 0.05)
        .text('Road House Magodo')
        .text('Magodo Shopping Arcade Ayodele \r\n Fanoki Magodo Phase I')
        .table(['item','qty','total'])
        load.load.forEach((element)=>{
          return printer.table(element)
        })
        printer.text(`Total:   ${load.total}`)
        if(load.change!='card'){
          printer.text(`Change:   ${load.change}`)
        }else{
          printer.text('Method: Card')
        }
        printer.newLine()
        printer.newLine()
        printer.cut()
        printer
        .font('a')
        .align('ct')
        .style('bu')
        .size(0.05, 0.05)
        .text('Road House Magodo')
        .text('Magodo Shopping Arcade Ayodele \r\n Fanoki Magodo Phase I')
        .table(['item','qty','total'])
        load.load.forEach((element)=>{
          return printer.table(element)
        })
        printer.text(`Total:   ${load.total}`)
        if(load.change!='card'){
          printer.text(`Change:   ${load.change}`)
        }else{
          printer.text('Method: Card')
        }
        printer.newLine()
        printer.cut()
        printer.close()
      },error=>{
        if(error){
          console.log(error)
          _message = error+'error'
          return
          }
      })
      return _message
}
const device  = new escpos.USB();
const printer = new escpos.Printer(device);
ipcMain.on('print', (_,load) => {
  let _fish =()=>{ 
    return print(JSON.parse(load))
  }
  _.reply(_fish());
})

basically passing your array or object and looping through it.