# Printing

OpenKedai prints receipts and kitchen tickets on ESC/POS thermal printers. It talks to printers
directly, with no printer apps or drivers to install, and printing never holds up a sale: if a
printer is off or out of paper, the job waits and retries, and the sale carries on.

## Printers that work

- **Network printers (LAN or Wi-Fi)** that accept raw ESC/POS on TCP port 9100. That is almost
  every receipt printer with an Ethernet or Wi-Fi port, including Xprinter (XP-58, XP-80,
  XP-Q200…), Epson TM series (TM-T82, TM-m30…), Rongta, Gprinter, HPRT and most unbranded
  58 mm and 80 mm models.
- **Sunmi built-in printers** on Sunmi Android terminals (T2, V2, D2 mini, the P-series and
  similar).

USB and Bluetooth printers are not supported yet.

## Adding a network printer

1. **Connect the printer** to the same network (router or Wi-Fi) as the tablet or phone.
2. **Find its IP address.** Turn the printer off, then hold the FEED button while turning it on,
   and release after about two seconds. It prints a self-test page that shows the IP address.
   Many Xprinter models come set to `192.168.123.100`. If that isn't on your network's range
   (for example `192.168.1.x`), change the printer's address in its web page, or turn on DHCP
   there and give it a fixed address in your router, so it doesn't change after a power cut.
3. In OpenKedai go to **Settings → Printers → Add printer** and enter:
   - a **name**, such as "Counter" or "Kitchen";
   - the **IP address**, with port 9100;
   - the **paper width**, 80 mm or 58 mm;
   - what it **prints**: receipts, kitchen tickets, or both.
4. Tap **Test print**. The page shows a ruler. If the ruler wraps onto a second line, lower
   **Characters per line**. If it stops well short of the edge, raise it.

On iPhone and iPad, the first test print asks for permission to find devices on your local
network. Allow it, or printing can't reach the printer.

## Settings explained

| Setting | Default | When to change it |
| --- | --- | --- |
| Characters per line | 48 (80 mm), 32 (58 mm) | Some 80 mm printers fit 42 |
| Cutter | Partial cut (80 mm), none (58 mm) | "Full cut" if the receipt doesn't come off cleanly; "No cutter" if there is none |
| QR codes | As image | "Printer's own" gives sharper codes on printers that support it; "Don't print" if QR codes come out as garbage |
| Chinese characters | As image | "Printer's own font" on a printer made for the Chinese market (GB18030): sharper and faster. Leave "As image" if Chinese comes out as garbage |
| Open the cash drawer | Off | Turn on if a cash drawer is plugged into this printer |

## Chinese and other languages

Receipts print in the shop's receipt language, with an optional second language for labels, and
kitchen tickets in the kitchen's language (back office **Shop settings → Receipt**, or the POS's
**Settings → Shop**). Z-reports and test pages print in the device's own language.

Chinese prints on any printer. By default each line with Chinese characters is sent as a small
image, drawn with GNU Unifont, so it works on printers without a Chinese font. Printers made for
the Chinese market have their own font; set **Chinese characters** to **Printer's own font** for
those. Simplified and traditional characters (GBK) are covered; anything else, such as emoji,
prints as "？".

## Kitchen tickets

Items go to the kitchen printers when you tap **Send to kitchen**, **Hold** or **Charge**. Only
new items are sent, so nothing prints twice. Drinks or goods that don't need preparing can be
excluded per item (Settings → Menu → item → "Print in kitchen"). Voiding an item that was
already sent prints a VOID ticket.

## When something goes wrong

If a job can't print, OpenKedai tries again after 2, 5, 15, 30 and 60 seconds. If it still
fails, a red **Printer problem** button appears on the register. **Settings → Printers** then
shows each recent job and why it failed, with a **Retry** button.

| Problem | Likely cause |
| --- | --- |
| "Can't reach the printer" | Printer off, wrong IP address, or the device is on a different network |
| Strange characters | The printer is in Chinese mode or uses another code page. OpenKedai resets this at the start of every job; if it persists, check the printer's settings page |
| QR code prints as random text | Set QR codes to "As image" |
| Receipt doesn't cut | Set the cutter to "Full cut", or "No cutter" if the printer has none |
| Lines wrap or look misaligned | Adjust characters per line with a test print |

## Sunmi terminals

On Sunmi devices, add a printer and choose **Built-in (Sunmi)**. OpenKedai uses Sunmi's own
print service, which comes preinstalled on these devices.

## Testing without a printer

Developers can run a pretend network printer on a computer on the same network:

```sh
node scripts/virtual-printer.ts
```

It listens on port 9100 and shows every job it receives as readable text. Add it in the app as
a network printer with the computer's IP address.
