111 lines
3.0 KiB
Python
111 lines
3.0 KiB
Python
from datetime import datetime
|
|
from typing import List
|
|
import unicodedata
|
|
|
|
import uvicorn
|
|
from fastapi import FastAPI, HTTPException, Header
|
|
from pydantic import BaseModel
|
|
|
|
from escpos.printer import Usb
|
|
from escpos.capabilities import get_profile
|
|
from PIL import Image
|
|
|
|
MAX_LINE_LEN = 32
|
|
SECRET_PASSWORD = "Alkohol1"
|
|
|
|
|
|
class OrderItem(BaseModel):
|
|
menu_item_name: str
|
|
amount: int
|
|
|
|
|
|
class Order(BaseModel):
|
|
order_id: str
|
|
order_date: datetime
|
|
customer_name: str
|
|
seat_id: str
|
|
items: List[OrderItem]
|
|
|
|
def get_printer() -> Usb:
|
|
return Usb(0x28e9, 0x0289, profile="simple")
|
|
|
|
def sanitize_text(text: str) -> str:
|
|
return unicodedata.normalize("NFKD", text).encode("ascii", "ignore").decode()
|
|
|
|
def build_header(order: Order, copy_num: int) -> str:
|
|
return (
|
|
"EZ GG e.V. - EZGG LAN 1.0\n"
|
|
"--------------------------------\n"
|
|
f"ID: {order.order_id}\n"
|
|
f"{order.order_date.strftime('%d.%m. %H:%M')}\n"
|
|
f"Gast: {sanitize_text(order.customer_name)}\n"
|
|
f"Sitz: {sanitize_text(order.seat_id)}\n"
|
|
f"Kopie: {copy_num}\n"
|
|
"--------------------------------\n"
|
|
)
|
|
|
|
|
|
def format_order(items: List[OrderItem]) -> List[str]:
|
|
lines = []
|
|
for item in items:
|
|
name = sanitize_text(item.menu_item_name)
|
|
line = f"{item.amount}x {name}"
|
|
if len(line) > MAX_LINE_LEN:
|
|
line = line[:MAX_LINE_LEN]
|
|
lines.append(line + "\n")
|
|
return lines
|
|
|
|
|
|
def print_logo(printer_: Usb, path: str = "logo.png"):
|
|
try:
|
|
img = Image.open(path)
|
|
max_width = 384
|
|
if img.width > max_width:
|
|
ratio = max_width / img.width
|
|
new_height = int(img.height * ratio)
|
|
img = img.resize((max_width, new_height))
|
|
printer_.set(align="center")
|
|
printer_.image(img)
|
|
printer_.text("\n")
|
|
except Exception:
|
|
printer_.text("[Logo error]\n")
|
|
|
|
|
|
def print_order(order: Order, printer_: Usb, copy_num: int) -> None:
|
|
printer_.set(align="center")
|
|
print_logo(printer_)
|
|
printer_.text(build_header(order, copy_num))
|
|
printer_.set(align="left")
|
|
for line in format_order(order.items):
|
|
printer_.text(line)
|
|
printer_.text("--------------------------------\n\n")
|
|
printer_.cut()
|
|
|
|
|
|
api = FastAPI()
|
|
|
|
|
|
@api.post("/print_order")
|
|
def print_order_api_endpoint(order: Order, x_password: str = Header(None)):
|
|
if x_password != SECRET_PASSWORD:
|
|
raise HTTPException(status_code=401, detail="Unauthorized")
|
|
|
|
for item in order.items:
|
|
if item.amount < 1:
|
|
raise HTTPException(status_code=422, detail="Invalid item amount")
|
|
|
|
for copy_num in range(1, 3):
|
|
try:
|
|
printer = get_printer()
|
|
print_order(order, printer, copy_num)
|
|
printer.close()
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=f"Printing failed: {str(e)}")
|
|
|
|
return {"status": "done"}
|
|
|
|
|
|
if __name__ == "__main__":
|
|
print("Starting receipt printing server...")
|
|
uvicorn.run(api, host="0.0.0.0", port=5000, log_level="warning")
|