From 1ac5df7be528681301ad6e359fbb42d77487c1db Mon Sep 17 00:00:00 2001 From: Jan Novak Date: Thu, 9 Apr 2026 12:48:15 +0200 Subject: [PATCH] chore: Remove archived pages (fees, reconcile) from web UI Deleted /fees, /fees-juniors, /reconcile, /reconcile-juniors routes and their templates. Payment Ledger (/payments) is retained. Nav updated across all remaining templates. Co-Authored-By: Claude Sonnet 4.6 --- app.py | 387 -------------- templates/adults.html | 4 - templates/fees-juniors.html | 253 --------- templates/fees.html | 268 ---------- templates/flush-cache.html | 4 - templates/juniors.html | 4 - templates/payments.html | 4 - templates/reconcile-juniors.html | 884 ------------------------------- templates/reconcile.html | 884 ------------------------------- templates/sync.html | 4 - 10 files changed, 2696 deletions(-) delete mode 100644 templates/fees-juniors.html delete mode 100644 templates/fees.html delete mode 100644 templates/reconcile-juniors.html delete mode 100644 templates/reconcile.html diff --git a/app.py b/app.py index d76763a..1b1ff29 100644 --- a/app.py +++ b/app.py @@ -157,151 +157,6 @@ def sync_bank(): def version(): return BUILD_META -@app.route("/fees") -def fees(): - attendance_url = f"https://docs.google.com/spreadsheets/d/{ATTENDANCE_SHEET_ID}/edit" - payments_url = f"https://docs.google.com/spreadsheets/d/{PAYMENTS_SHEET_ID}/edit" - - members_data = get_cached_data("attendance_regular", ATTENDANCE_SHEET_ID, get_members_with_fees) - record_step("fetch_members") - if not members_data: - return "No data." - members, sorted_months = members_data - - # Filter to adults only for display - results = [(name, fees) for name, tier, fees in members if tier == "A"] - - # Format month labels - month_labels = get_month_labels(sorted_months, ADULT_MERGED_MONTHS) - - monthly_totals = {m: 0 for m in sorted_months} - - # Get exceptions for formatting - credentials_path = CREDENTIALS_PATH - exceptions = get_cached_data( - "exceptions_dict", PAYMENTS_SHEET_ID, fetch_exceptions, - PAYMENTS_SHEET_ID, credentials_path, - serialize=lambda d: [[list(k), v] for k, v in d.items()], - deserialize=lambda c: {tuple(k): v for k, v in c}, - ) - record_step("fetch_exceptions") - - formatted_results = [] - for name, month_fees in results: - row = {"name": name, "months": []} - norm_name = normalize(name) - for m in sorted_months: - fee, count = month_fees.get(m, (0, 0)) - - # Check for exception - norm_period = normalize(m) - ex_data = exceptions.get((norm_name, norm_period)) - override_amount = ex_data["amount"] if ex_data else None - - if override_amount is not None and override_amount != fee: - cell = f"{override_amount} ({fee}) CZK ({count})" if count > 0 else f"{override_amount} ({fee}) CZK" - is_overridden = True - else: - if isinstance(fee, int): - monthly_totals[m] += fee - cell = f"{fee} CZK ({count})" if count > 0 else "-" - is_overridden = False - row["months"].append({"cell": cell, "overridden": is_overridden}) - formatted_results.append(row) - - record_step("process_data") - - return render_template( - "fees.html", - months=[month_labels[m] for m in sorted_months], - results=formatted_results, - totals=[f"{monthly_totals[m]} CZK" for m in sorted_months], - attendance_url=attendance_url, - payments_url=payments_url - ) - -@app.route("/fees-juniors") -def fees_juniors(): - attendance_url = f"https://docs.google.com/spreadsheets/d/{ATTENDANCE_SHEET_ID}/edit#gid={JUNIOR_SHEET_GID}" - payments_url = f"https://docs.google.com/spreadsheets/d/{PAYMENTS_SHEET_ID}/edit" - - members_data = get_cached_data("attendance_juniors", ATTENDANCE_SHEET_ID, get_junior_members_with_fees) - record_step("fetch_junior_members") - if not members_data: - return "No data." - members, sorted_months = members_data - - # Sort members by name - results = sorted([(name, fees) for name, tier, fees in members], key=lambda x: x[0]) - - # Format month labels - month_labels = get_month_labels(sorted_months, JUNIOR_MERGED_MONTHS) - - monthly_totals = {m: 0 for m in sorted_months} - - # Get exceptions for formatting (reusing payments sheet) - credentials_path = CREDENTIALS_PATH - exceptions = get_cached_data( - "exceptions_dict", PAYMENTS_SHEET_ID, fetch_exceptions, - PAYMENTS_SHEET_ID, credentials_path, - serialize=lambda d: [[list(k), v] for k, v in d.items()], - deserialize=lambda c: {tuple(k): v for k, v in c}, - ) - record_step("fetch_exceptions") - - formatted_results = [] - for name, month_fees in results: - row = {"name": name, "months": []} - norm_name = normalize(name) - for m in sorted_months: - fee_data = month_fees.get(m, (0, 0, 0, 0)) - if len(fee_data) == 4: - fee, total_count, adult_count, junior_count = fee_data - else: - fee, total_count = fee_data - adult_count, junior_count = 0, 0 - - # Check for exception - norm_period = normalize(m) - ex_data = exceptions.get((norm_name, norm_period)) - override_amount = ex_data["amount"] if ex_data else None - - if ex_data is None and isinstance(fee, int): - monthly_totals[m] += fee - - # Formulate the count string display - if adult_count > 0 and junior_count > 0: - count_str = f"{total_count} ({adult_count}A+{junior_count}J)" - elif adult_count > 0: - count_str = f"{total_count} (A)" - elif junior_count > 0: - count_str = f"{total_count} (J)" - else: - count_str = f"{total_count}" - - if override_amount is not None and override_amount != fee: - cell = f"{override_amount} ({fee}) CZK / {count_str}" if total_count > 0 else f"{override_amount} ({fee}) CZK" - is_overridden = True - else: - if fee == "?": - cell = f"? / {count_str}" if total_count > 0 else "-" - else: - cell = f"{fee} CZK / {count_str}" if total_count > 0 else "-" - is_overridden = False - row["months"].append({"cell": cell, "overridden": is_overridden}) - formatted_results.append(row) - - record_step("process_data") - - return render_template( - "fees-juniors.html", - months=[month_labels[m] for m in sorted_months], - results=formatted_results, - totals=[f"{t} CZK" if isinstance(t, int) else t for t in monthly_totals.values()], - attendance_url=attendance_url, - payments_url=payments_url - ) - @app.route("/adults") def adults_view(): attendance_url = f"https://docs.google.com/spreadsheets/d/{ATTENDANCE_SHEET_ID}/edit" @@ -442,110 +297,6 @@ def adults_view(): bank_account=BANK_ACCOUNT ) -@app.route("/reconcile") -def reconcile_view(): - attendance_url = f"https://docs.google.com/spreadsheets/d/{ATTENDANCE_SHEET_ID}/edit" - payments_url = f"https://docs.google.com/spreadsheets/d/{PAYMENTS_SHEET_ID}/edit" - - # Use hardcoded credentials path for now, consistent with other scripts - credentials_path = CREDENTIALS_PATH - - members_data = get_cached_data("attendance_regular", ATTENDANCE_SHEET_ID, get_members_with_fees) - record_step("fetch_members") - if not members_data: - return "No data." - members, sorted_months = members_data - - transactions = get_cached_data("payments_transactions", PAYMENTS_SHEET_ID, fetch_sheet_data, PAYMENTS_SHEET_ID, credentials_path) - record_step("fetch_payments") - exceptions = get_cached_data( - "exceptions_dict", PAYMENTS_SHEET_ID, fetch_exceptions, - PAYMENTS_SHEET_ID, credentials_path, - serialize=lambda d: [[list(k), v] for k, v in d.items()], - deserialize=lambda c: {tuple(k): v for k, v in c}, - ) - record_step("fetch_exceptions") - result = reconcile(members, sorted_months, transactions, exceptions) - record_step("reconcile") - - # Format month labels - month_labels = get_month_labels(sorted_months, ADULT_MERGED_MONTHS) - - # Filter to adults for the main table - adult_names = sorted([name for name, tier, _ in members if tier == "A"]) - - formatted_results = [] - for name in adult_names: - data = result["members"][name] - row = {"name": name, "months": [], "balance": data["total_balance"], "unpaid_periods": "", "raw_unpaid_periods": ""} - unpaid_months = [] - raw_unpaid_months = [] - for m in sorted_months: - mdata = data["months"].get(m, {"expected": 0, "original_expected": 0, "paid": 0}) - expected = mdata["expected"] - paid = int(mdata["paid"]) - - status = "empty" - cell_text = "-" - amount_to_pay = 0 - - 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 - unpaid_months.append(month_labels[m]) - raw_unpaid_months.append(datetime.strptime(m, "%Y-%m").strftime("%m/%Y")) - else: - status = "unpaid" - cell_text = f"UNPAID {expected}" - amount_to_pay = expected - unpaid_months.append(month_labels[m]) - raw_unpaid_months.append(datetime.strptime(m, "%Y-%m").strftime("%m/%Y")) - 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], - "raw_month": m - }) - - row["unpaid_periods"] = ", ".join(unpaid_months) if unpaid_months else ("Older debt" if data["total_balance"] < 0 else "") - row["raw_unpaid_periods"] = "+".join(raw_unpaid_months) - row["balance"] = data["total_balance"] # Updated to use total_balance - formatted_results.append(row) - - # Format credits and debts - credits = sorted([{"name": n, "amount": a["total_balance"]} for n, a in result["members"].items() if a["total_balance"] > 0 and n in adult_names], key=lambda x: x["name"]) - debts = sorted([{"name": n, "amount": abs(a["total_balance"])} for n, a in result["members"].items() if a["total_balance"] < 0 and n in adult_names], key=lambda x: x["name"]) - # Format unmatched - unmatched = result["unmatched"] - import json - - record_step("process_data") - - return render_template( - "reconcile.html", - months=[month_labels[m] for m in sorted_months], - raw_months=sorted_months, - results=formatted_results, - member_data=json.dumps(result["members"]), - month_labels_json=json.dumps(month_labels), - credits=credits, - debts=debts, - unmatched=unmatched, - attendance_url=attendance_url, - payments_url=payments_url, - bank_account=BANK_ACCOUNT - ) - @app.route("/juniors") def juniors_view(): attendance_url = f"https://docs.google.com/spreadsheets/d/{ATTENDANCE_SHEET_ID}/edit#gid={JUNIOR_SHEET_GID}" @@ -723,144 +474,6 @@ def juniors_view(): bank_account=BANK_ACCOUNT ) -@app.route("/reconcile-juniors") -def reconcile_juniors_view(): - attendance_url = f"https://docs.google.com/spreadsheets/d/{ATTENDANCE_SHEET_ID}/edit#gid={JUNIOR_SHEET_GID}" - payments_url = f"https://docs.google.com/spreadsheets/d/{PAYMENTS_SHEET_ID}/edit" - - credentials_path = CREDENTIALS_PATH - - junior_members_data = get_cached_data("attendance_juniors", ATTENDANCE_SHEET_ID, get_junior_members_with_fees) - record_step("fetch_junior_members") - if not junior_members_data: - return "No data." - junior_members, sorted_months = junior_members_data - - transactions = get_cached_data("payments_transactions", PAYMENTS_SHEET_ID, fetch_sheet_data, PAYMENTS_SHEET_ID, credentials_path) - record_step("fetch_payments") - exceptions = get_cached_data( - "exceptions_dict", PAYMENTS_SHEET_ID, fetch_exceptions, - PAYMENTS_SHEET_ID, credentials_path, - serialize=lambda d: [[list(k), v] for k, v in d.items()], - deserialize=lambda c: {tuple(k): v for k, v in c}, - ) - record_step("fetch_exceptions") - - # Adapt junior tuple format (name, tier, {month: (fee, total_count, adult_count, junior_count)}) - # to what match_payments expects: (name, tier, {month: (expected_fee, attendance_count)}) - adapted_members = [] - for name, tier, fees_dict in junior_members: - adapted_fees = {} - for m, fee_data in fees_dict.items(): - if len(fee_data) == 4: - fee, total_count, _, _ = fee_data - adapted_fees[m] = (fee, total_count) - else: - fee, count = fee_data - adapted_fees[m] = (fee, count) - adapted_members.append((name, tier, adapted_fees)) - - result = reconcile(adapted_members, sorted_months, transactions, exceptions) - record_step("reconcile") - - # Format month labels - month_labels = get_month_labels(sorted_months, JUNIOR_MERGED_MONTHS) - - # Filter to juniors for the main table - junior_names = sorted([name for name, tier, _ in adapted_members]) - - junior_members_dict_rc = {name: fees_dict for name, _, fees_dict in junior_members} - - formatted_results = [] - for name in junior_names: - data = result["members"][name] - row = {"name": name, "months": [], "balance": data["total_balance"], "unpaid_periods": "", "raw_unpaid_periods": ""} - unpaid_months = [] - raw_unpaid_months = [] - for m in sorted_months: - mdata = data["months"].get(m, {"expected": 0, "original_expected": 0, "paid": 0}) - expected = mdata["expected"] - paid = int(mdata["paid"]) - - orig_fee_data = junior_members_dict_rc.get(name, {}).get(m) - adult_count = 0 - junior_count = 0 - att_count = 0 - if orig_fee_data and len(orig_fee_data) == 4: - _, att_count, adult_count, junior_count = orig_fee_data - - breakdown = "" - if adult_count > 0 and junior_count > 0: - breakdown = f":{junior_count}J,{adult_count}A" - elif junior_count > 0: - breakdown = f":{junior_count}J" - elif adult_count > 0: - breakdown = f":{adult_count}A" - - count_str = f" ({att_count}{breakdown})" if att_count > 0 else "" - - status = "empty" - cell_text = "-" - amount_to_pay = 0 - - if expected == "?" or (isinstance(expected, int) and expected > 0): - if expected == "?": - status = "empty" - cell_text = f"?{count_str}" - elif paid >= expected: - status = "ok" - cell_text = "OK" - elif paid > 0: - status = "partial" - cell_text = f"{paid}/{expected}" - amount_to_pay = expected - paid - unpaid_months.append(month_labels[m]) - raw_unpaid_months.append(datetime.strptime(m, "%Y-%m").strftime("%m/%Y")) - else: - status = "unpaid" - cell_text = f"UNPAID {expected}" - amount_to_pay = expected - unpaid_months.append(month_labels[m]) - raw_unpaid_months.append(datetime.strptime(m, "%Y-%m").strftime("%m/%Y")) - 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], - "raw_month": m - }) - - row["unpaid_periods"] = ", ".join(unpaid_months) if unpaid_months else ("Older debt" if data["total_balance"] < 0 else "") - row["raw_unpaid_periods"] = "+".join(raw_unpaid_months) - row["balance"] = data["total_balance"] - formatted_results.append(row) - - # Format credits and debts - credits = sorted([{"name": n, "amount": a["total_balance"]} for n, a in result["members"].items() if a["total_balance"] > 0], key=lambda x: x["name"]) - debts = sorted([{"name": n, "amount": abs(a["total_balance"])} for n, a in result["members"].items() if a["total_balance"] < 0], key=lambda x: x["name"]) - import json - - record_step("process_data") - - return render_template( - "reconcile-juniors.html", - months=[month_labels[m] for m in sorted_months], - raw_months=sorted_months, - results=formatted_results, - member_data=json.dumps(result["members"]), - month_labels_json=json.dumps(month_labels), - credits=credits, - debts=debts, - unmatched=[], - attendance_url=attendance_url, - payments_url=payments_url, - bank_account=BANK_ACCOUNT - ) - @app.route("/payments") def payments(): attendance_url = f"https://docs.google.com/spreadsheets/d/{ATTENDANCE_SHEET_ID}/edit" diff --git a/templates/adults.html b/templates/adults.html index 46a4ca2..e02740a 100644 --- a/templates/adults.html +++ b/templates/adults.html @@ -458,10 +458,6 @@