feat: implement local payment QR codes and update AI co-authoring rules
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:
78
app.py
78
app.py
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user