feat: multi-account Fio sync + switch QR default to 2502035405/2010
All checks were successful
Deploy to K8s / deploy (push) Successful in 24s
All checks were successful
Deploy to K8s / deploy (push) Successful in 24s
Add second Fio account (CZ0820100000002502035405 / 2502035405/2010). Both accounts are fetched on every sync run and combined before dedup, so the payments sheet accumulates transactions from either account. QR codes now default to the new account. Go: - config.go: hardcoded Accounts/LoadedAccount slice replaces scalar BankAccount + FioAPIToken; Config.BankAccount renamed QRAccount; per-account tokens via FIO_API_TOKEN_NEW / FIO_API_TOKEN_OLD - banksync.SyncToSheets: accepts []fio.Client, loops to combine txns - cmd/fuj/main.go: buildFioClients helper; both sync call sites updated - html_handler + build_adults/juniors: use Config.QRAccount - New TestSyncToSheets_MultiAccount covers cross-account dedup Python: - config.py: ACCOUNTS list + LOADED_ACCOUNTS (tokens from env) - fio_utils.py: fetch_transactions_for (per-account) + fetch_transactions_all (loops all accounts) - sync_fio_to_sheets.py: uses fetch_transactions_all Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -21,8 +21,18 @@ PAYMENTS_SHEET_ID = "1Om0YPoDVCH5cV8BrNz5LG5eR5MMU05ypQC7UMN1xn_Y"
|
||||
# Attendance sheet tab GIDs
|
||||
JUNIOR_SHEET_GID = "1213318614"
|
||||
|
||||
# Bank
|
||||
BANK_ACCOUNT = os.environ.get("BANK_ACCOUNT", "CZ8520100000002800359168")
|
||||
# Bank accounts — hardcoded list mirroring go/internal/config/config.go.
|
||||
# The entry with primary=True is used for QR codes and as the BANK_ACCOUNT default.
|
||||
ACCOUNTS = [
|
||||
{"iban": "CZ0820100000002502035405", "acct_num": "2502035405", "token_env": "FIO_API_TOKEN_NEW", "primary": True},
|
||||
{"iban": "CZ8520100000002800359168", "acct_num": "2800359168", "token_env": "FIO_API_TOKEN_OLD", "primary": False},
|
||||
]
|
||||
# Resolve API tokens from the environment once at import time.
|
||||
LOADED_ACCOUNTS = [
|
||||
{**a, "token": os.environ.get(a["token_env"], "")}
|
||||
for a in ACCOUNTS
|
||||
]
|
||||
BANK_ACCOUNT = next(a["iban"] for a in ACCOUNTS if a["primary"])
|
||||
|
||||
# Cache settings
|
||||
CACHE_DIR = PROJECT_ROOT / "tmp"
|
||||
|
||||
@@ -103,7 +103,7 @@ def parse_czech_date(s: str) -> str | None:
|
||||
|
||||
|
||||
def fetch_transactions_transparent(
|
||||
date_from: str, date_to: str, account_id: str = "2800359168"
|
||||
date_from: str, date_to: str, account_id: str
|
||||
) -> list[dict]:
|
||||
"""Fetch transactions from Fio transparent account HTML page.
|
||||
|
||||
@@ -206,22 +206,47 @@ def fetch_transactions_api(
|
||||
return transactions
|
||||
|
||||
|
||||
def fetch_transactions(date_from: str, date_to: str) -> list[dict]:
|
||||
"""Fetch transactions, using API if token available, else transparent page."""
|
||||
token = os.environ.get("FIO_API_TOKEN", "").strip()
|
||||
def fetch_transactions_for(account: dict, date_from: str, date_to: str) -> list[dict]:
|
||||
"""Fetch transactions for a single loaded account dict (from config.LOADED_ACCOUNTS).
|
||||
|
||||
Uses the API path if the account has a token, otherwise the transparent scraper.
|
||||
date_from/date_to: YYYY-MM-DD.
|
||||
"""
|
||||
token = (account.get("token") or "").strip()
|
||||
acct_num = account["acct_num"]
|
||||
if token:
|
||||
print(f"fio: using authenticated API, window {date_from}..{date_to}", file=sys.stderr)
|
||||
print(f"fio: account {acct_num}: using authenticated API, window {date_from}..{date_to}", file=sys.stderr)
|
||||
return fetch_transactions_api(token, date_from, date_to)
|
||||
|
||||
print(
|
||||
f"fio: using transparent page (FIO_API_TOKEN unset — expect publishing lag), "
|
||||
f"window {date_from}..{date_to}, account=2800359168",
|
||||
f"fio: account {acct_num}: using transparent page (no token — expect publishing lag), "
|
||||
f"window {date_from}..{date_to}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
# Convert YYYY-MM-DD to DD.MM.YYYY for the transparent page URL
|
||||
from_dt = datetime.strptime(date_from, "%Y-%m-%d")
|
||||
to_dt = datetime.strptime(date_to, "%Y-%m-%d")
|
||||
return fetch_transactions_transparent(
|
||||
from_dt.strftime("%d.%m.%Y"),
|
||||
to_dt.strftime("%d.%m.%Y"),
|
||||
account_id=acct_num,
|
||||
)
|
||||
|
||||
|
||||
def fetch_transactions_all(
|
||||
date_from: str, date_to: str, accounts: list[dict] | None = None
|
||||
) -> list[dict]:
|
||||
"""Fetch and combine transactions from all configured accounts.
|
||||
|
||||
accounts: list of loaded account dicts (defaults to config.LOADED_ACCOUNTS).
|
||||
Returns a flat list of all transactions across all accounts.
|
||||
"""
|
||||
if accounts is None:
|
||||
from config import LOADED_ACCOUNTS
|
||||
accounts = LOADED_ACCOUNTS
|
||||
all_txns: list[dict] = []
|
||||
for account in accounts:
|
||||
txns = fetch_transactions_for(account, date_from, date_to)
|
||||
print(f"fio: account {account['acct_num']}: {len(txns)} transaction(s)", file=sys.stderr)
|
||||
all_txns.extend(txns)
|
||||
print(f"fio: total {len(all_txns)} transaction(s) across {len(accounts)} account(s)", file=sys.stderr)
|
||||
return all_txns
|
||||
|
||||
@@ -12,7 +12,7 @@ from google_auth_oauthlib.flow import InstalledAppFlow
|
||||
from google.oauth2 import service_account
|
||||
from googleapiclient.discovery import build
|
||||
|
||||
from fio_utils import fetch_transactions
|
||||
from fio_utils import fetch_transactions_all
|
||||
|
||||
from config import PAYMENTS_SHEET_ID as DEFAULT_SPREADSHEET_ID
|
||||
SCOPES = ["https://www.googleapis.com/auth/spreadsheets"]
|
||||
@@ -188,7 +188,7 @@ def sync_to_sheets(
|
||||
dt_str = date_to.strftime("%Y-%m-%d")
|
||||
|
||||
print(f"Fetching Fio transactions from {df_str} to {dt_str}...")
|
||||
transactions = fetch_transactions(df_str, dt_str)
|
||||
transactions = fetch_transactions_all(df_str, dt_str)
|
||||
print(f"Found {len(transactions)} transactions.")
|
||||
|
||||
if dry_run:
|
||||
|
||||
Reference in New Issue
Block a user