feat: implement local payment QR codes and update AI co-authoring rules
All checks were successful
Deploy to K8s / deploy (push) Successful in 11s
Build and Push / build (push) Successful in 8s

QR codes are now generated locally using the 'qrcode' library for better privacy and reliability.
Updated .agent/rules.md with co-author details and Conventional Commits preference.

Co-authored-by: Antigravity <antigravity@google.com>
This commit is contained in:
Jan Novak
2026-03-02 22:54:48 +01:00
parent b0276f68b3
commit 4bb8c7420c
3 changed files with 188 additions and 19 deletions

78
app.py
View File

@@ -3,7 +3,10 @@ from pathlib import Path
from datetime import datetime
import re
import time
from flask import Flask, render_template, g
import os
import io
import qrcode
from flask import Flask, render_template, g, send_file, request
# Add scripts directory to path to allow importing from it
scripts_dir = Path(__file__).parent / "scripts"
@@ -14,6 +17,9 @@ from match_payments import reconcile, fetch_sheet_data, fetch_exceptions, normal
app = Flask(__name__)
# Bank account for QR code payments (can be overridden by ENV)
BANK_ACCOUNT = os.environ.get("BANK_ACCOUNT", "CZ8520100000002800359168")
@app.before_request
def start_timer():
g.start_time = time.perf_counter()
@@ -141,20 +147,34 @@ def reconcile_view():
for m in sorted_months:
mdata = data["months"].get(m, {"expected": 0, "original_expected": 0, "paid": 0})
expected = mdata["expected"]
original = mdata["original_expected"]
paid = int(mdata["paid"])
cell_status = ""
if expected == 0 and paid == 0:
cell = "-"
elif paid >= expected and expected > 0:
cell = "OK"
elif paid > 0:
cell = f"{paid}/{expected}"
else:
cell = f"UNPAID {expected}"
status = "empty"
cell_text = "-"
amount_to_pay = 0
row["months"].append(cell)
if expected > 0:
if paid >= expected:
status = "ok"
cell_text = "OK"
elif paid > 0:
status = "partial"
cell_text = f"{paid}/{expected}"
amount_to_pay = expected - paid
else:
status = "unpaid"
cell_text = f"UNPAID {expected}"
amount_to_pay = expected
elif paid > 0:
status = "surplus"
cell_text = f"PAID {paid}"
row["months"].append({
"text": cell_text,
"status": status,
"amount": amount_to_pay,
"month": month_labels[m]
})
row["balance"] = data["total_balance"] # Updated to use total_balance
formatted_results.append(row)
@@ -178,7 +198,8 @@ def reconcile_view():
debts=debts,
unmatched=unmatched,
attendance_url=attendance_url,
payments_url=payments_url
payments_url=payments_url,
bank_account=BANK_ACCOUNT
)
@app.route("/payments")
@@ -221,5 +242,36 @@ def payments():
payments_url=payments_url
)
@app.route("/qr")
def qr_code():
account = request.args.get("account", BANK_ACCOUNT)
amount = request.args.get("amount", "0")
message = request.args.get("message", "")
# QR Platba standard: SPD*1.0*ACC:accountNumber*BC:bankCode*AM:amount*CC:CZK*MSG:message
acc_parts = account.split('/')
if len(acc_parts) == 2:
acc_str = f"{acc_parts[0]}*BC:{acc_parts[1]}"
else:
acc_str = account
try:
amt_val = float(amount)
amt_str = f"{amt_val:.2f}"
except ValueError:
amt_str = "0.00"
# Message max 60 characters
msg_str = message[:60]
qr_data = f"SPD*1.0*ACC:{acc_str}*AM:{amt_str}*CC:CZK*MSG:{msg_str}"
img = qrcode.make(qr_data)
buf = io.BytesIO()
img.save(buf, format='PNG')
buf.seek(0)
return send_file(buf, mimetype='image/png')
if __name__ == "__main__":
app.run(debug=True, host='0.0.0.0', port=5001)