A standalone Python service that polls a remote Frappe site for unprinted Pick Notes, fetches the rendered print format output, converts it to plain text, and sends it to a local thermal printer. Includes a modern web dashboard for configuration, monitoring, and control.
- Overview
- Prerequisites
- Quick Start
- Frappe Setup
- Configuration
- Printer Setup
- Running as a Service
- Dashboard
- API Endpoints
- Print Format
- Troubleshooting
- License
┌──────────────────┐ REST API ┌────────────────────┐
│ Frappe Site │ ◄──────────────── │ Print Service │
│ (ERPNext) │ ──────────────► │ (Python/Flask) │
│ │ Pick Notes │ │
└──────────────────┘ │ ┌──────────────┐ │
│ │ APScheduler │ │
│ │ Poll Worker │ │
│ └──────┬───────┘ │
│ │ │
│ Raw bytes │
│ │ │
│ ┌──────▼───────┐ │
│ │ Printer │ │
│ │ Module │ │
│ └──────┬───────┘ │
└─────────┼──────────┘
│
┌─────────▼──────────┐
│ Thermal Printer │
│ (USB/Network/ │
│ Serial/Windows) │
└────────────────────┘
How it works:
- The service polls Frappe every N seconds for unprinted Pick Notes.
- For each unprinted Pick Note, it fetches the rendered print format output.
- HTML content is stripped and converted to plain text suitable for thermal printers.
- The plain text is encoded and sent to the configured thermal printer.
- On successful printing, the Pick Note is marked as "Printed" on the Frappe site.
| Requirement | Version | Notes |
|---|---|---|
| Python | 3.9+ | 3.10+ recommended |
| Frappe | v14+ | With the Pick Note Print API installed |
| Thermal Printer | — | USB, Network (ESC/POS), Serial, or Windows driver |
| API Keys | — | Generated from the Frappe User settings |
The service expects the following API endpoint module on your Frappe site:
amex.amex.utils.pick_note_print_api
This module should expose these whitelisted methods:
get_unprinted_pick_notes— Returns unprinted Pick Notesget_pick_note_print_raw— Returns rendered print format outputmark_pick_note_printed— Marks a Pick Note as printed
# 1. Clone or copy the project
# 2. Run the setup script
setup_windows.bat
# 3. Start the service
venv\Scripts\activate
python app.py
# 4. Open the dashboard
# Navigate to http://localhost:5555# 1. Clone or copy the project
# 2. Make the setup script executable and run it
chmod +x setup_linux.sh
./setup_linux.sh
# 3. Start the service
source venv/bin/activate
python app.py
# 4. Open the dashboard
# Navigate to http://localhost:5555- Log in to your Frappe site as an Administrator.
- Go to User → select the user that will authenticate the print service.
- Scroll down to API Access section.
- Click Generate Keys.
- Copy the API Secret immediately — it will not be shown again.
- Copy the API Key from the same section.
The API user needs the following permissions:
- Read access to the Pick Note DocType
- Write access to the Pick Note DocType (to mark as printed)
- Access to the custom API methods in
amex.amex.utils.pick_note_print_api
All settings are stored in config.json in the application directory. You can configure everything through the web dashboard at http://localhost:5555.
| Setting | Type | Default | Description |
|---|---|---|---|
frappe_url |
string | "" |
Root URL of your Frappe site (e.g. https://erp.example.com) |
api_key |
string | "" |
Frappe API key |
api_secret |
string | "" |
Frappe API secret |
printer_name |
string | "" |
Name of the selected printer |
printer_type |
string | "windows" |
"windows", "network", "serial", or "cups" |
printer_address |
string | "" |
IP:port (network) or device path (serial) |
poll_interval_seconds |
int | 30 |
How often to check for new Pick Notes (minimum: 5) |
print_format |
string | "" |
Optional Frappe print format name |
Use the Windows printer driver. The service sends raw data using win32print.
- Install the printer driver from the manufacturer.
- Ensure the printer appears in Settings → Printers & Scanners.
- In the dashboard, select Printer Type: Windows Printer.
- Click Refresh to list printers, then select your thermal printer.
Connect directly via TCP/IP to an ESC/POS compatible thermal printer.
- Find your printer's IP address (check printer settings or router).
- The default ESC/POS port is 9100.
- In the dashboard, select Printer Type: Network (IP).
- Enter the address as
192.168.1.100:9100.
Connect via a serial/COM port.
| OS | Example Address |
|---|---|
| Windows | COM3 |
| Linux | /dev/ttyUSB0 |
- Connect the printer via USB-to-serial or RS-232.
- In the dashboard, select Printer Type: Serial.
- Enter the device path.
Use CUPS (Common UNIX Printing System) for USB-connected printers on Linux.
- Install CUPS:
sudo apt install cups - Add your printer via CUPS web interface (
http://localhost:631) orlpadmin. - Verify with:
lpstat -p - In the dashboard, select Printer Type: USB / CUPS (Linux).
- Click Refresh to list CUPS printers and select yours.
NSSM (Non-Sucking Service Manager) is the recommended way to run the service in the background on Windows.
# Download NSSM from https://nssm.cc/download
# Then install the service:
nssm install PickNotePrintService "C:\path\to\pick_note_print_service\venv\Scripts\python.exe" "C:\path\to\pick_note_print_service\app.py"
nssm set PickNotePrintService AppDirectory "C:\path\to\pick_note_print_service"
nssm set PickNotePrintService DisplayName "Pick Note Print Service"
nssm set PickNotePrintService Description "Automatic Pick Note thermal printing service"
nssm set PickNotePrintService Start SERVICE_AUTO_START
# Start the service
nssm start PickNotePrintService
# To remove the service later:
nssm remove PickNotePrintService confirmCreate a systemd unit file:
# /etc/systemd/system/pick-note-print.service
[Unit]
Description=Pick Note Print Service
After=network.target
[Service]
Type=simple
User=your-username
WorkingDirectory=/path/to/pick_note_print_service
ExecStart=/path/to/pick_note_print_service/venv/bin/python app.py
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
Environment=PYTHONUNBUFFERED=1
[Install]
WantedBy=multi-user.target# Enable and start the service
sudo systemctl daemon-reload
sudo systemctl enable pick-note-print.service
sudo systemctl start pick-note-print.service
# Check status
sudo systemctl status pick-note-print.service
# View logs
sudo journalctl -u pick-note-print.service -fThe web dashboard is accessible at http://localhost:5555 and provides:
- Connection Settings — Configure and test the Frappe site connection
- Print Settings — Configure print layout settings, optional print format, and poll interval
- Printer Settings — Select printer type, enumerate local printers, test printing
- Service Controls — Start/Stop the background worker, view stats
- Print Log — Real-time table of recent print jobs with status and timestamps
The log panel auto-refreshes every 10 seconds.
| Method | Endpoint | Description |
|---|---|---|
GET |
/ |
Dashboard HTML page |
GET |
/api/config |
Get current configuration (secret masked) |
POST |
/api/config |
Save configuration |
POST |
/api/test-connection |
Test Frappe connection with provided credentials |
GET |
/api/printers |
List locally available printers |
POST |
/api/service/start |
Start the print worker |
POST |
/api/service/stop |
Stop the print worker |
GET |
/api/status |
Get worker status (running, last poll, errors, jobs count) |
GET |
/api/logs |
Get recent print job logs |
POST |
/api/test-print |
Send a test print to the configured printer |
For the best results with thermal printers, create a dedicated Frappe Print Format:
- Keep it narrow — Thermal printers are typically 58mm or 80mm wide (32 or 48 characters per line for monospace).
- Use monospace fonts —
Courier Newor similar for aligned columns. - Minimal HTML — The service strips HTML to plain text. Use simple structures.
- Use
<br>for line breaks — They are converted to newlines. - Use
<table>for columns —</td>is converted to tab spacing,</tr>to newlines.
<div style="font-family: Courier New, monospace; font-size: 12px; width: 280px;">
<div style="text-align: center;">
<b>{{ doc.company }}</b><br>
<b>PICK NOTE</b><br>
{{ doc.name }}<br>
--------------------------------
</div>
<br>
Date: {{ doc.posting_date }}<br>
--------------------------------<br>
<table>
<tr>
<td><b>Item</b></td>
<td><b>Qty</b></td>
</tr>
{% for item in doc.items %}
<tr>
<td>{{ item.item_code }}</td>
<td>{{ item.qty }}</td>
</tr>
{% endfor %}
</table>
--------------------------------<br>
<div style="text-align: center;">
Printed: {{ frappe.utils.now() }}
</div>
</div>| Problem | Solution |
|---|---|
| Connection refused | Verify the Frappe URL is correct and accessible from this machine. Check firewalls. |
| Authentication failed (HTTP 401/403) | Verify API key and secret. Ensure the user has the required permissions. |
| SSL certificate error | If using self-signed certs, you may need to set verify=False in the session (not recommended for production). |
| Timeout | Check network connectivity. The default timeout is 10s for connect, 30s for read. |
| Problem | Solution |
|---|---|
| No printers found | Windows: Check printer driver installation. Linux: Check CUPS is installed and running (sudo systemctl status cups). |
| Permission denied | Linux: Add your user to the lp group: sudo usermod -aG lp $USER |
| Network printer timeout | Verify IP address and port. Test with: nc -zv 192.168.1.100 9100 |
| Garbled output | Ensure the printer supports raw/text mode. Check the encoding (UTF-8 vs ASCII). |
| Serial port access denied | Linux: sudo chmod 666 /dev/ttyUSB0 or add user to dialout group. Windows: Check COM port in Device Manager. |
| Problem | Solution |
|---|---|
| Service won't start | Check that all configuration fields are filled (URL, keys, printer). Check the terminal output for errors. |
| Pick Notes not printing | Check that unprinted Pick Notes exist. Look at the print log for errors. |
| Duplicate prints | The service marks Pick Notes as printed after successful dispatch. If the Frappe API call to mark as printed fails, duplicates may occur. Check network stability. |
MIT License
Copyright (c) 2024
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.