#!/usr/bin/env python3 # -*- coding: utf-8 -*- import sys import traceback import urllib.parse import qrcode from pathlib import Path from datetime import datetime, date from PIL import Image, ImageTk import customtkinter as ctk from tkinter import messagebox if not getattr(sys, "frozen", False): _project_root = Path(__file__).resolve().parent.parent.parent.parent sys.path.insert(0, str(_project_root)) from Knihovny.najdi_dropbox import get_dropbox_root from Knihovny.EmailMessagingGraph import send_mail DROPBOX = Path(get_dropbox_root()) 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 = DROPBOX / "OrdinaceProjekt" / "Medicus" / "Externi" / "QRCode" / "QRPlatby" OUTPUT_DIR.mkdir(exist_ok=True) from Knihovny.medicus_db import get_medicus_connection # 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 = get_medicus_connection() 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} Kč") 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)