feat: multi-account Fio sync + switch QR default to 2502035405/2010
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:
2026-05-24 21:42:47 +02:00
parent 7f801d27f5
commit 69af4c1e3b
13 changed files with 431 additions and 39 deletions

View File

@@ -88,7 +88,7 @@ func serverCmd(args []string) {
fmt.Fprintf(os.Stderr, "fuj server: sheets client for sync: %v\n", err)
os.Exit(1)
}
fioCli := fio.New(cfg.FioAPIToken, config.IBANAccountNum(cfg.BankAccount), nil)
fioClients := buildFioClients(cfg)
actions := web.ActionHandlers{
BankSync: func(ctx context.Context, out io.Writer) error {
@@ -97,7 +97,7 @@ func serverCmd(args []string) {
to := time.Date(yr, 12, 31, 23, 59, 59, 0, time.UTC)
fmt.Fprintln(out, "=== Sync Fio Transactions ===")
n, err := banksync.SyncToSheets(ctx, config.PaymentsSheetID, fioCli, sheetsCli,
n, err := banksync.SyncToSheets(ctx, config.PaymentsSheetID, fioClients, sheetsCli,
banksync.SyncOpts{From: from, To: to, Sort: true})
if err != nil {
return fmt.Errorf("sync: %w", err)
@@ -197,7 +197,7 @@ func syncCmd(args []string) {
fmt.Fprintf(os.Stderr, "fuj sync: sheets client: %v\n", err)
os.Exit(1)
}
fioCli := fio.New(cfg.FioAPIToken, config.IBANAccountNum(cfg.BankAccount), nil)
fioClients := buildFioClients(cfg)
opts := banksync.SyncOpts{Days: *days, Sort: *sort, DryRun: *dryRun, PrintFioTable: *printFioTable}
if *fromStr != "" && *toStr != "" {
@@ -213,7 +213,7 @@ func syncCmd(args []string) {
}
}
n, err := banksync.SyncToSheets(ctx, config.PaymentsSheetID, fioCli, sheetsCli, opts)
n, err := banksync.SyncToSheets(ctx, config.PaymentsSheetID, fioClients, sheetsCli, opts)
if err != nil {
fmt.Fprintf(os.Stderr, "fuj sync: %v\n", err)
os.Exit(1)
@@ -274,3 +274,13 @@ Commands:
sync Sync Fio transactions to payments sheet
infer Infer payment details in payments sheet`)
}
// buildFioClients constructs one fio.Client per configured account.
// Each client uses the account's token if available, otherwise the transparent-scraper path.
func buildFioClients(cfg config.Config) []fio.Client {
clients := make([]fio.Client, 0, len(cfg.LoadedAccounts))
for _, a := range cfg.LoadedAccounts {
clients = append(clients, fio.New(a.Token, a.AcctNum, nil))
}
return clients
}