用JavaScript自己做POS出單功能

RenZhou
7 min readJul 16, 2020

全端工程師開飲料店,當然POS系統就要自己做啦~!!

鑑於以前大學時到處打工的經歷,POS 系統也用過滿多廠商的😅😅。不過本篇的主軸就不放在 POS 系統上面了。畢竟只要能點餐能結帳,就是好POS ?

總而言之,軟體方面是沒啥大問題,目前是用 Vue (前端)+ Laravel (後端)來做,比較有問題就是硬體出單的方面,總要印取餐單以及飲料上的標籤吧。

流程構想:

點餐平板 → (傳送訂單資訊) → 列印伺服器 → (加任務至佇列) →出單機

完成品

從買出單機開始

出單機都是熱感式印表機 (也就是不用墨水光靠熱感紙就能瘋狂輸出的印表機)。打關鍵字會找到一堆賣 POS系統 的廠商,因為都沒寫價格所以乾脆去露天找。找下來其實價格從2,990~8,000都有…。為了節省成本我就買最便宜的 2,990 還沒寫廠牌只寫說大廠製造,留言問客服有沒有支援 Linux ,客服也不清楚,反正便宜我就買了。

如果不是跟我一樣買這台,可以省略下面幾步

http://www.xprinter.net/index.php/Product/product_detail/id/43/cid2/13/cid/3

遇到第一個問題

貨到了,才發現這貨驅動沒有 Linux 、Mac 的啊啊啊啊!!!而且在 CUPS 中看到的廠牌名稱還是 富士 Xerox ,但是這公司網站找不到這款產品。只好用這台機器的名字上網去找 Xprinter XP-235B。原來是中國某公司的產品,在產品頁面的驅動載下來就是一個exe的安裝包,但我是的環境不能執行exe啊!

感謝國外網友問題解決了…一半?

後來爬文的過程找到了一篇文章,裡面提到其實可以安裝其他廠牌的驅動,一樣可以印(實在是太神奇了),於是我高興的拿著我之前買的 Raspberry pi 4 想說這麼小台,直接拿來當遠端列印伺服器應該蠻適合的。

遇到第二個麻煩

由於 Raspberry pi 4 的 CPU 是 ARM 架構,導致安裝程序完全無法運行。後來想說驅動檔案先裝在其他電腦用複製過去貼上,結果列印時會出現驅動錯誤。試了一陣子,果斷生出一台 Ubuntu Server 20.04 的電腦出來充當主機。

安裝流程概述

要用 Linux 或是 Mac 列印的話,我們要先安裝 CUPS (Common Unix Printing System),來當作我們的列印伺服器,這東西在 Mac 跟 Raspberry pi 也能裝,只是 Raspberry pi 驅動裝不上去真的可惜了。安裝指令也非常簡單:

sudo apt install cups# 把自己加入印表機的管理員群組
sudo usermod -aG lpadmin <your username>

然後打開瀏覽器網址輸入以下網址,就能看到CUPS 的頁面啦

http://localhost:631
CUPS 的首頁畫面

接下來就照上面外國網友提供的方式,幫這台印表機安裝其他廠牌的驅動,並在 CUPS 頁面上新增這台印表機,在硬體安裝部分差不多就告一個段落。

列印的方式

要做一個體驗流暢的 POS系統,在結帳時跑出網頁的預覽列印頁面出來感覺就很影響體驗,最好是按鈕按下去就直接給我印出來。其次就是用網頁做的話要印出內容還要寫很多CSS,而且也會造成前端的一些負擔。於是就規劃出一個負責列印的伺服器,前端只要將要印的東西透過 POST 丟到伺服器來,然後由伺服器將資料套成 PDF,最後在印出這個 PDF 就行啦!!

就決定是你了,NodeJS

要快速建一個能吃 POST 的伺服器,那就是 Express JS 啦,不用寫幾行就能搭一個出來。而且剛好 NodeJS 也都有符合我們需求的插件。不過首先先想辦法讓他能印才行。

先試看看能不能印

這邊要裝幾個插件 pdfkit、fs:

# 用 JS 產生 PDF 的插件
npm install pdfkit
# 讓 JS 能存取檔案的插件
npm install fs

關於 PDFKit 的詳細使用方式可以參考官方文件,在這邊我們先建立一個測試的檔案 test.js

// test.jsconst PDFDocument = require('pdfkit')
const fs = require('fs')
// 新增一個 PDF (這邊調整成標籤紙的大小,單位是 pt)
// 紙張 size 要自己算一下,我的印表機大小錯誤會吐 error
const doc = new PDFDocument({
size: [114, 85],
margin: 0
})
doc.fontSize(20).text(`TEST`, 0, 35, { align: 'center'})
doc.pipe(fs.createWriteStream('./test.pdf'))
doc.end()

注意:不同作業系統字體大小可能會不太一樣

然後下指令執行,根目錄就會生出 test.pdf 啦

node test.js
test.pdf

串上印表機

要多安裝兩個插件 ipp、concat-stream

npm i ipp concat-stream

然後完整程式碼如下,裡面的 ipp.Printer 的網址為

http://<CUPS主機所在網址>/printers/<CUPS上的印表機名稱>例如: http://127.0.0.1:631/printers/Xerox_Xprinter_XP-235B

到這邊下指令執行就會印出來囉~!!

再來就是加上 Express

能印之後,加上 Express 並且調整好要列印的樣式,就能做到前端丟請求直接印出來的效果啦。加上 Express 的過程並不難,這邊我就不做教學啦~XD

調整好格式都先輸出一份pdf來看,沒問題再印比較不會浪費紙哦~

特別要注意的地方

  1. PDFKit 使用預設字型會導致中文字變成亂碼

可以自行找一些字型檔 (.ttf)透過 doc.font(<字型路徑>) 來替換,但有些字型會漏字或是出現奇怪的雜點,因此多試幾個就能找出適合的字型囉。

// 指定字型
doc.font(`statics/fonts/rttf.ttf`)

2. 標籤的紙張定位跑掉導致印表機吐ERROR

因為不是用原廠驅動,所以在印的時候他不會自己修正大小。所以要像我在建立 PDF 時就給他計算好的紙張大小(特別留意:單位是 pt,請自己上網找資料換算 XD),才不會導致列印跑版或是錯誤的情形發生。

3. 要買機器來玩真的不推這台啊!!

想自己動手寫 POS 的朋友,別像我省成本買這台啊~~~最好是買大廠牌並且有支援 Linux 驅動的,才不會跟我現在一樣裝別牌的驅動湊合著用…。至於印表機有沒有內建藍牙或是無線列印,其實照我的做法是沒這個必要多花錢買這些功能,因為我們自己做了😅

4. 除了印貼紙還能印取餐單

因為一樣都是用熱感紙,所以只要調好列印格式就能印,但是同時要印兩種還是要買兩台。我另一台是跟朋友借的 Star TSP143GT,有原廠驅動跟說明文件只能說裝起來就是舒服

給客人的取餐單

大致上就這樣啦~ 順便業配一下自己的店!!

加店裡的 LINE 好友直接請你喝一杯 ❤

--

--