Files
KPK/tisky.py
T
2026-06-23 15:20:56 +02:00

252 lines
7.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from PySide6.QtWidgets import (
QDialog, QVBoxLayout, QTextEdit,
QPushButton, QHBoxLayout, QPlainTextEdit
)
from PySide6.QtGui import QFont
from PySide6.QtCore import Qt
import os
import builtins
import socket
def open_printer_from_setup(setup):
if setup.printer_type == "tcp":
...
#return open_printer_tcp(
# setup.printer_host,
# setup.printer_port or 9100)
def show_receipt_preview(parent, ucet, width: int = 40):
dlg = QDialog(parent)
dlg.setWindowTitle("Náhled účtenky")
dlg.setModal(True)
dlg.resize(600, 780)
main = QVBoxLayout(dlg)
main.setSpacing(10)
main.setContentsMargins(10, 10, 10, 10)
# =====================================================
# TEXT ÚČTENKY MONOSPACE (VYNUCENÝ)
# =====================================================
receipt = QPlainTextEdit()
receipt.setReadOnly(True)
receipt.setLineWrapMode(QPlainTextEdit.NoWrap)
# 🔒 VYNUCENÝ MONOSPACE žádné QSS to nepřebije
font = QFont()
font.setFamily("Consolas")
font.setStyleHint(QFont.Monospace)
font.setFixedPitch(True)
font.setPointSize(11)
receipt.setFont(font)
# pojistka proti QSS
receipt.setStyleSheet("""
QPlainTextEdit {
font-family: Consolas, "Courier New", monospace;
font-size: 11pt;
}
""")
receipt.setPlainText("\n".join(format_uctu_text(ucet, width)))
main.addWidget(receipt, stretch=1)
# =====================================================
# SPODNÍ TOUCH PANEL
# =====================================================
bottom = QHBoxLayout()
bottom.setSpacing(12)
BTN_W = 180
BTN_H = 70
btn_print = QPushButton("TISK")
btn_print.setEnabled(False)
btn_print.setFixedSize(BTN_W, BTN_H)
btn_print.setStyleSheet("""
QPushButton {
font-size: 18px;
font-weight: bold;
background: #CCCCCC;
color: #666666;
}
""")
btn_close = QPushButton("ZAVŘÍT")
btn_close.setFixedSize(BTN_W, BTN_H)
btn_close.setStyleSheet("""
QPushButton {
font-size: 18px;
font-weight: bold;
background: #E6B3B3;
color: black;
}
QPushButton:pressed {
background: #D99A9A;
}
""")
btn_close.clicked.connect(dlg.accept)
bottom.addStretch(1)
bottom.addWidget(btn_print)
bottom.addWidget(btn_close)
main.addLayout(bottom)
dlg.exec()
def format_uctu_str( ucet, width: int = 40) -> str:
return "\n".join(format_uctu_text(ucet, width))
def format_uctu_text(ucet, width: int = 40) -> list[str]:
lines: list[str] = []
def line(left: str = "", right: str = ""):
#Zarovná left vlevo a right doprava na pevnou šířku
if right:
space = width - len(left) - len(right)
if space < 1:
space = 1
lines.append((left + " " * space + right)[:width])
else:
lines.append(left[:width])
def sep(ch: str = "-"):
lines.append(ch * width)
discount = round(getattr(ucet, "discount_abs", 0.0) or 0.0, 2)
# ================= HLAVIČKA =================
sep("=")
dt = ucet.closed_at or ucet.datetime or ""
lines.append(dt.center(width))
cislo = ucet.ucislo or "-"
lines.append(f"UCET {cislo}".center(width))
# STORNO OZNAČENÍ
if getattr(ucet, "is_storno", None):
lines.append(" STORNO ".center(width))
if ucet.autor:
lines.append(f"Obsluha: {ucet.autor}".center(width))
sep("=")
# ================= POLOŽKY =================
total = 0.0
total_before_discount = 0.0
for p in ucet.poloz:
# je to dělená porce?
delene = bool(p.delitel and p.delitel != 1)
if delene:
kusu = p.pocet / p.delitel
qty_txt = f"{p.pocet}×1/{p.delitel}" # např. 2×1/2
else:
kusu = p.pocet
qty_txt = str(p.pocet)
cena = round(p.cena * kusu, 2)
cena_before = round((p.cena_puv if getattr(p, "cena_puv", None) is not None else p.cena) * kusu, 2)
total += cena
total_before_discount += cena_before
# ====== JEDEN ŘÁDEK jen NEDĚLENÝ 1 KS ======
if not delene and kusu == 1:
left = p.nazev
right = f"{cena:.2f}"
line(left.ljust(width - len(right)) + right)
# ====== DĚLENÉ NEBO VÍCE KS → VŽDY DVA ŘÁDKY ======
else:
line(p.nazev[:width])
left = f"{qty_txt} x {p.cena:.2f}"
right = f"{cena:.2f}"
line(left.ljust(width - len(right)) + right)
# ================= SOUČET =================
sep("-")
prorated = getattr(ucet, "discounts_prorated", False)
payable = total if prorated else total - discount
subtotal_for_print = total_before_discount if prorated else total
line("MEZISOUČET", f"{subtotal_for_print:.2f} Kč")
if discount != 0:
if discount > 0:
line("SLEVA", f"-{discount:.2f} Kč")
else:
line("PRIRAZKA", f"{-discount:.2f} Kč")
sep("-")
line("K ÚHRADĚ", f"{payable:.2f} Kč")
else:
line("K ÚHRADĚ", f"{payable:.2f} Kč")
# ================= DPH =================
sep("-")
for d in ucet.dane:
# d.rate např. "0.21"
_, pct = d.rate.split(".", 1)
pct = pct.ljust(2, "0")
zaklad = round(d.zaklad, 2)
dph_castka = round(d.zaklad * float(d.rate), 2)
left = f"DPH {pct} % Zaklad {zaklad:.2f}"
right = f"{dph_castka:.2f}"
line(left, right)
# ================= PLATBY =================
if getattr(ucet, "platby", None):
sep("-")
for p in ucet.platby:
if p.unit != "CZK" and p.rate != 1:
left = f"{p.nazev} {p.suma:.2f} {p.unit}"
right = f"{p.suma * p.rate:.2f}"
else:
left = p.nazev
right = f"{p.suma:.2f}"
line(left, right)
sep("=")
return lines
def normalize_cp852(text: str) -> str:
replacements = {
"—": "-", "": "-", "": "-",
"“": '"', "”": '"', "„": '"',
"": "'", "": "'",
"…": "...",
"\u00a0": " ", # NBSP
"č": "c", "ě": "e", "š": "s", "ř": "r",
"ž": "z", "ý": "y", "á": "a", "í": "i",
"é": "e", "ú": "u", "ů": "u",
}
for k, v in replacements.items():
text = text.replace(k, v)
return text
def tisk_uctu( ucet, printer, *, width: int = 40,
init_cmd: bytes = b"\x1b@",
cut_cmd: bytes = b"\x1dV\x00",):
# inicializace tiskárny
if init_cmd:
printer.write(init_cmd)
for l in format_uctu_text(ucet, width):
safe = normalize_cp852(l)
printer.write((safe + "\n").encode("cp852", errors="replace"))
# odřez
if cut_cmd:
printer.write(cut_cmd)
class Printer:
def __init__(self, stream):
self.stream = stream
def write(self, data: bytes):
self.stream.write(data)
def close(self):
if hasattr(self.stream, "close"):
self.stream.close()
class ConsolePrinter(Printer):
def write(self, data: bytes):
data = data.replace(b"\x1b", b"<ESC>")
print(data.decode("ascii", errors="replace"), end="")