Files
ordinaceprojekt/Medicus/Externi/QRCode/QRPlatbaApp.py
T
Vladimir Buzalka eb8a370ecb notebookvb
2026-05-10 07:51:20 +02:00

239 lines
8.2 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.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
import traceback
import urllib.parse
import qrcode
import fdb
from pathlib import Path
from datetime import datetime, date
from PIL import Image, ImageTk
import customtkinter as ctk
from tkinter import messagebox
PROJECT_ROOT = Path(r"U:\OrdinaceProjekt")
sys.path.insert(0, str(PROJECT_ROOT))
from Knihovny.EmailMessagingGraph import send_mail
EMAIL_CHYBY = "vladimir.buzalka@buzalka.cz"
def _zpracuj_chybu(exc: BaseException):
detail = traceback.format_exc()
try:
root = ctk.CTk()
root.withdraw()
messagebox.showerror("Chyba — QRPlatbaApp",
f"Nastala chyba:\n{exc}\n\nByla odeslána na email.")
root.destroy()
except Exception:
pass
try:
send_mail(
to=EMAIL_CHYBY,
subject=f"QRPlatbaApp — chyba ({date.today().isoformat()})",
body=f"Pacient: {PRIJMENI} {JMENO} / RC: {RODCIS}\n\n{detail}",
)
except Exception:
pass
# ================================
# ⚙️ Default Configuration
# ================================
ACCOUNTS = {
"2100046291/2010": "CZ1720100000002100046291",
"2800046620/2010": "CZ7520100000002800046620",
}
CURRENCY = "CZK"
OUTPUT_DIR = Path(r"U:\OrdinaceProjekt\Medicus\Externi\QRCode\QRPlatby")
OUTPUT_DIR.mkdir(exist_ok=True)
# Firebird připojení
DB_DSN = r'localhost:c:\medicus 3\data\medicus.fdb'
DB_USER = 'SYSDBA'
DB_PASSWORD = 'masterkey'
DB_CHARSET = 'win1250'
# Default values (can be overridden by arguments)
PRIJMENI = "Buzalka"
JMENO = "Vladimír"
RODCIS = "730928104"
# ================================
# 💬 Argument Handling
# ================================
if len(sys.argv) >= 4:
JMENO = sys.argv[1]
PRIJMENI = sys.argv[2]
RODCIS = sys.argv[3].replace("/", "")
elif len(sys.argv) == 2 and sys.argv[1] in ("-h", "--help"):
print("Usage: QRPlatbaApp.py JMENO PRIJMENI RODCIS")
sys.exit(0)
# ================================
# 💉 Items to Pay načteno z Medicusu
# ================================
def nacti_polozky():
"""Načte ceník z Medicusu seřazený podle KOD (pořadového čísla)."""
try:
conn = fdb.connect(dsn=DB_DSN, user=DB_USER, password=DB_PASSWORD, charset=DB_CHARSET)
cur = conn.cursor()
cur.execute("SELECT V.KOD, V.NAZEV, V.CENA FROM VLV_SEL(NULL, NULL, NULL) V ORDER BY V.KOD")
rows = cur.fetchall()
conn.close()
# Vrátí OrderedDict: název -> cena (float)
return {row[1].strip(): float(row[2]) for row in rows if row[2] is not None}
except Exception as e:
messagebox.showerror("Chyba databáze", f"Nepodařilo se načíst ceník z Medicusu:\n{e}")
return {}
ITEMS = nacti_polozky()
# ================================
# 🧩 Helper
# ================================
def create_spayd(iban, amount, vs, msg, currency="CZK"):
msg_encoded = urllib.parse.quote(msg, safe="$%*+-.:/")
return f"SPD*1.0*ACC:{iban}*AM:{amount:.2f}*CC:{currency}*X-VS:{vs}*MSG:{msg_encoded}"
# ================================
# 🪟 GUI Class
# ================================
class QRPlatbaApp(ctk.CTk):
def __init__(self):
super().__init__()
self.title("QR Platba Ordinace MUDr. Buzalková")
self.geometry("520x680")
self.minsize(480, 480)
self.resizable(True, True)
ctk.set_appearance_mode("light")
ctk.set_default_color_theme("blue")
frame = ctk.CTkFrame(self, corner_radius=10)
frame.pack(expand=True, fill="both", padx=20, pady=20)
ctk.CTkLabel(frame, text="Generátor QR Platby",
font=("Arial", 20, "bold")).pack(pady=(8, 10))
# 👤 Patient Info
patient = ctk.CTkFrame(frame, corner_radius=8)
patient.pack(fill="x", pady=(0, 8), padx=10)
for text in [f"Příjmení: {PRIJMENI}",
f"Jméno: {JMENO}",
f"Rodné číslo: {RODCIS}"]:
ctk.CTkLabel(patient, text=text, font=("Arial", 12)).pack(anchor="w", padx=10, pady=1)
# 💰 Payment Section
pay = ctk.CTkFrame(frame, corner_radius=8)
pay.pack(fill="x", pady=(0, 8), padx=10)
ctk.CTkLabel(pay, text="Vyberte položku k úhradě:",
font=("Arial", 12, "bold")).pack(anchor="w", padx=10, pady=(6, 3))
self.display_items = [f"{name} ({price:.0f} Kč)" for name, price in ITEMS.items()]
self.item_map = {f"{name} ({price:.0f} Kč)": name for name, price in ITEMS.items()}
self.selected_item = ctk.StringVar(value=self.display_items[0])
self.combo = ctk.CTkOptionMenu(
pay,
variable=self.selected_item,
values=self.display_items,
font=("Arial", 12),
command=self.on_change
)
self.combo.pack(fill="x", padx=10)
self.amount_label = ctk.CTkLabel(pay, text="", font=("Arial", 12, "italic"))
self.amount_label.pack(anchor="e", padx=10, pady=(3, 6))
# 🏦 Account Selection
acc = ctk.CTkFrame(frame, corner_radius=8)
acc.pack(fill="x", pady=(0, 8), padx=10)
ctk.CTkLabel(acc, text="Číslo účtu:", font=("Arial", 12, "bold")).pack(anchor="w", padx=10, pady=(6, 3))
self.selected_account = ctk.StringVar(value=list(ACCOUNTS.keys())[0])
ctk.CTkOptionMenu(
acc,
variable=self.selected_account,
values=list(ACCOUNTS.keys()),
font=("Arial", 12),
command=self.on_change
).pack(fill="x", padx=10, pady=(0, 6))
ctk.CTkButton(frame, text="Uložit QR kód",
font=("Arial", 13, "bold"),
height=40,
command=self.ulozit_qr).pack(pady=6)
self.qr_label = ctk.CTkLabel(frame, text="")
self.qr_label.pack(pady=6)
ctk.CTkLabel(frame,
text="© Ordinace MUDr. Buzalková | QR Platba dle ČBA v1.2",
font=("Arial", 10),
text_color="#666").pack(side="bottom", pady=(10, 0))
self.center_window()
# QR automaticky při startu
self.after(100, self.refresh_qr)
# ================================
# 🪟 Center Window
# ================================
def center_window(self):
self.update_idletasks()
width = self.winfo_width()
height = self.winfo_height()
screen_width = self.winfo_screenwidth()
screen_height = self.winfo_screenheight()
x = int((screen_width / 2) - (width / 2))
y = int((screen_height / 2) - (height / 2))
self.geometry(f"{width}x{height}+{x}+{y}")
# ================================
# 💸 Update and Generate
# ================================
def _get_current(self):
"""Vrátí (item, iban, spayd) pro aktuální výběr."""
display_item = self.selected_item.get()
item = self.item_map[display_item]
iban = ACCOUNTS[self.selected_account.get()]
spayd = create_spayd(iban, ITEMS[item], RODCIS, f"{PRIJMENI} {JMENO} {item}", CURRENCY)
return item, iban, spayd
def on_change(self, _=None):
"""Při změně dropdownu aktualizuj částku i QR."""
self.refresh_qr()
def refresh_qr(self):
"""Zobrazí QR kód pro aktuální výběr (bez uložení)."""
item, _, spayd = self._get_current()
self.amount_label.configure(text=f"Částka: {ITEMS[item]:.2f}")
img = qrcode.make(spayd)
img_resized = img.resize((200, 200), Image.LANCZOS)
qr_tk = ImageTk.PhotoImage(img_resized)
self.qr_label.configure(image=qr_tk)
self.qr_label.image = qr_tk
def ulozit_qr(self):
"""Uloží QR kód do souboru a informuje uživatele."""
item, _, spayd = self._get_current()
img = qrcode.make(spayd)
filename = f"{PRIJMENI}_{JMENO}_{datetime.now():%Y%m%d_%H%M%S}.png"
out_path = OUTPUT_DIR / filename
img.save(out_path)
messagebox.showinfo("Uloženo", f"QR kód uložen:\n{out_path}")
# ================================
# 🚀 Main
# ================================
if __name__ == "__main__":
try:
app = QRPlatbaApp()
app.mainloop()
except Exception as exc:
_zpracuj_chybu(exc)
sys.exit(1)