From 0375982137b122baf4b9964ce49bdb20e599f377 Mon Sep 17 00:00:00 2001 From: Vladimir Buzalka Date: Wed, 5 Nov 2025 19:54:51 +0100 Subject: [PATCH] notbook --- .idea/vcs.xml | 4 +- 010 IBAN validator.py | 0 010qrcodegenerator.py | 205 ++++++++++++++++++++++++++++++++++++++++++ 011 test.py | 109 ++++++++++++++++++++++ 4 files changed, 317 insertions(+), 1 deletion(-) delete mode 100644 010 IBAN validator.py create mode 100644 010qrcodegenerator.py create mode 100644 011 test.py diff --git a/.idea/vcs.xml b/.idea/vcs.xml index d843f34..94a25f7 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,4 +1,6 @@ - + + + \ No newline at end of file diff --git a/010 IBAN validator.py b/010 IBAN validator.py deleted file mode 100644 index e69de29..0000000 diff --git a/010qrcodegenerator.py b/010qrcodegenerator.py new file mode 100644 index 0000000..7aa96ee --- /dev/null +++ b/010qrcodegenerator.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +============================================ +QR Platba Generator – Fully Documented Version +============================================ + +This script generates a valid Czech payment QR code (QR Platba) +according to the official ČBA (Czech Banking Association) specification v1.2. + +The script creates a correct SPD (Short Payment Descriptor) string, +turns it into a QR code (PNG image), and saves it to disk. + +✅ Works with all Czech banks (Fio, ČSOB, KB, Air Bank…) +✅ Supports both IBAN or domestic account format (account/bankcode) +✅ Automatically URL-encodes the message (so spaces and diacritics work) +✅ Can handle optional VS, SS, KS, due date, and instant-payment flag + +Usage example (in your terminal): +--------------------------------- +python generate_qrplatba.py \ + --acc CZ7520100000002800046620 \ + --amount 1543.50 \ + --vs 7309208104 \ + --ss 123456 \ + --ks 0308 \ + --date 09.12.2025 \ + --msg "ahoj zlatíčko" \ + --out qr_ok.png +""" + +import argparse # For parsing command-line arguments +import urllib.parse # For safe URL-encoding of message text +from pathlib import Path # For easy file handling (cross-platform) +import qrcode # For generating the QR code image +from datetime import datetime # For date parsing and formatting + + +# -------------------------------------------------------------------- +# 🧩 Helper function: Normalize date into the required YYYYMMDD format +# -------------------------------------------------------------------- +def normalize_date(date_str: str) -> str: + """ + Converts a user-supplied date string (various formats) into the + standard YYYYMMDD format used by the QR Platba specification. + + Acceptable input examples: + "20251209" + "09.12.2025" + "2025-12-09" + "09122025" + + Returns: + str: normalized date string in "YYYYMMDD" format + or empty string "" if input is None or empty + """ + if not date_str: + return "" + s = date_str.strip() + + # Already in correct 8-digit form? + if len(s) == 8 and s.isdigit(): + return s + + # Try several common formats + for fmt in ("%d.%m.%Y", "%Y-%m-%d", "%d%m%Y"): + try: + dt = datetime.strptime(s, fmt) + return dt.strftime("%Y%m%d") + except ValueError: + continue + + # If all parsing attempts fail → raise an informative error + raise ValueError( + "Date must be in one of formats: YYYYMMDD, DD.MM.YYYY, YYYY-MM-DD, DDMMYYYY" + ) + + +# -------------------------------------------------------------------- +# 🔧 Helper function: Build the SPD string (core of QR Platba) +# -------------------------------------------------------------------- +def build_spayd(params: dict) -> str: + """ + Assembles a valid SPD (Short Payment Descriptor) string + according to ČBA v1.2 specification. + + Example output: + SPD*1.0*ACC:CZ7520100000002800046620*AM:1543.50*CC:CZK*DT:20251209* + X-VS:7309208104*X-SS:123456*X-KS:0308*MSG:ahoj%20zlaticko + """ + + # Start with fixed header + parts = ["SPD*1.0*"] + + # --- Required field: account number / IBAN --- + acc = params.get("acc", "").strip() + if not acc: + raise ValueError("Missing required parameter: --acc") + parts.append(f"ACC:{acc}*") + + # --- Optional: amount --- + amt = params.get("amount") + if amt: + parts.append(f"AM:{float(amt):.2f}*") # always two decimals + + # --- Optional: currency (defaults to CZK) --- + cc = params.get("cc") or "CZK" + parts.append(f"CC:{cc}*") + + # --- Optional: due date --- + dt = params.get("dt") + if dt: + parts.append(f"DT:{dt}*") + + # --- Optional: symbols (VS, SS, KS) --- + for key in ("X-VS", "X-SS", "X-KS"): + val = params.get(key.replace("-", "_").lower()) + if val: + parts.append(f"{key}:{val}*") + + # --- Optional: payment type (PT:IP = Instant Payment) --- + pt = params.get("pt") + if pt: + parts.append(f"PT:{pt}*") + + # --- Optional: message for recipient --- + msg = params.get("msg") or "" + if msg: + # Encode to keep spaces and Czech letters valid + safe_chars = "$%*+-.:/" + encoded_msg = urllib.parse.quote(msg, safe=safe_chars) + parts.append(f"MSG:{encoded_msg}") + + # Combine everything into one string + return "".join(parts) + + +# -------------------------------------------------------------------- +# 🚀 Main program entry point +# -------------------------------------------------------------------- +def main(): + # ------------------------------- + # Define all command-line options + # ------------------------------- + parser = argparse.ArgumentParser( + description="Generate a Czech QR Platba (payment QR code)." + ) + parser.add_argument("--acc", required=True, + help='Account: either "2800046620/2010" or IBAN "CZ..."') + parser.add_argument("--amount", help="Payment amount (e.g. 1543.50)") + parser.add_argument("--vs", help="Variable symbol (X-VS)") + parser.add_argument("--ss", help="Specific symbol (X-SS)") + parser.add_argument("--ks", help="Constant symbol (X-KS)") + parser.add_argument("--date", help="Due date (YYYYMMDD or DD.MM.YYYY etc.)") + parser.add_argument("--msg", help="Message for recipient (will be URL-encoded)") + parser.add_argument("--cc", default="CZK", help="Currency (default CZK)") + parser.add_argument("--pt", help="Payment type (e.g. IP for instant payment)") + parser.add_argument("--out", default="qrplatba.png", help="Output PNG file name") + + args = parser.parse_args() + + # ------------------------------- + # Normalize and prepare arguments + # ------------------------------- + dt_norm = normalize_date(args.date) if args.date else "" + + params = { + "acc": args.acc, + "amount": args.amount, + "cc": args.cc, + "dt": dt_norm, + "x_vs": args.vs, + "x_ss": args.ss, + "x_ks": args.ks, + "msg": args.msg, + "pt": args.pt, + } + + # ------------------------------- + # Build the SPD string + # ------------------------------- + spayd = build_spayd(params) + + # ------------------------------- + # Generate QR code image + # ------------------------------- + img = qrcode.make(spayd) + output_path = Path(args.out) + img.save(output_path) + + # ------------------------------- + # Print results for verification + # ------------------------------- + print("✅ QR Platba successfully generated") + print("SPD string (you can verify at https://qr-platba.cz/test/):") + print(spayd) + print() + print(f"📂 QR code image saved to: {output_path.resolve()}") + + +# -------------------------------------------------------------------- +# 🔘 Run when executed directly +# -------------------------------------------------------------------- +if __name__ == "__main__": + main() diff --git a/011 test.py b/011 test.py new file mode 100644 index 0000000..99279d1 --- /dev/null +++ b/011 test.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import urllib.parse +import qrcode +from pathlib import Path +from datetime import datetime +from PIL import Image, ImageTk +import customtkinter as ctk +from tkinter import messagebox + +IBAN = "CZ7520100000002800046620" +CURRENCY = "CZK" +OUTPUT_DIR = Path("QRPlatby") +OUTPUT_DIR.mkdir(exist_ok=True) + +PRIJMENI = "Buzalka" +JMENO = "Vladimír" +RODCIS = "730928104" + +ITEMS = { + "Očkování chřipka Vaxigrip": 600.00, + "Očkování chřipka Efluelda": 1300.00, +} + +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}" + +class QRPlatbaApp(ctk.CTk): + def __init__(self): + super().__init__() + self.title("QR Platba – Ordinace MUDr. Buzalková") + self.geometry("520x520") + 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=(10, 20)) + + patient = ctk.CTkFrame(frame, corner_radius=8) + patient.pack(fill="x", pady=(0, 20), 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=2) + + pay = ctk.CTkFrame(frame, corner_radius=8) + pay.pack(fill="x", pady=(0, 20), padx=10) + ctk.CTkLabel(pay, text="Vyberte položku k úhradě:", + font=("Arial", 12, "bold")).pack(anchor="w", padx=10, pady=(10, 5)) + + self.selected_item = ctk.StringVar(value=list(ITEMS.keys())[0]) + self.combo = ctk.CTkOptionMenu(pay, + variable=self.selected_item, + values=list(ITEMS.keys()), + font=("Arial", 12), + command=self.update_amount) + 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=(5, 10)) + self.update_amount() + + ctk.CTkButton(frame, text="Vytvořit QR Platbu", + font=("Arial", 13, "bold"), + height=40, + command=self.generate_qr).pack(pady=10) + + self.qr_label = ctk.CTkLabel(frame, text="") + self.qr_label.pack(pady=15) + + 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)) + + def update_amount(self, _=None): + item = self.selected_item.get() + self.amount_label.configure(text=f"Částka: {ITEMS[item]:.2f} Kč") + + def generate_qr(self): + item = self.selected_item.get() + spayd = create_spayd(IBAN, ITEMS[item], RODCIS, f"{PRIJMENI} {JMENO} – {item}", CURRENCY) + 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) + + img_resized = img.resize((300, 300)) + qr_tk = ImageTk.PhotoImage(img_resized) + self.qr_label.configure(image=qr_tk) + self.qr_label.image = qr_tk + + # 🔄 Adjust window height dynamically + self.update_idletasks() + self.geometry(f"{self.winfo_reqwidth()}x{self.winfo_reqheight()}") + + messagebox.showinfo("QR Platba vytvořena", + f"Soubor uložen jako:\n{out_path}\n\nSPD řetězec:\n{spayd}") + +if __name__ == "__main__": + app = QRPlatbaApp() + app.mainloop()