upload/download-artifact@v4 is not supported on Gitea (GHES). Replace
with a direct Gitea API call in gitops-update: look up the tag name
whose commit SHA matches workflow_run.head_sha. Reverts the artifact
upload from build.yaml; no changes to build.yaml logic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
github.event.workflow_run.head_branch is not populated for tag pushes
in Gitea Actions, causing the image tag to resolve to empty (-go suffix
with no version). Fix: build-go uploads the full image reference as a
one-line artifact; gitops-update downloads it via download-artifact@v4
with run-id from the workflow_run event.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
tea pr create matches the remote URL against the configured login URL to
auto-detect owner/repo. Embedding credentials in the URL (user:token@host)
breaks that match and produces "path segment [0] is empty". Store creds
via git credential helper instead and pass a clean URL to uh-cli.
Also adds set -x to the PR step for shell-level tracing in CI logs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
After a successful Go image build, uh-cli opens a PR against
kacerr/home-kubernetes that bumps the fuj-management Deployment
(namespace fuj) to the newly published image tag. Supports
workflow_run auto-trigger and workflow_dispatch with dry-run option.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Configured-month cases now read expected values from AdultFeeMonthlyRate /
JuniorFeeMonthlyRate via a mustRate helper that panics if a test month is
removed from the map. Fallback cases use AdultFeeDefault / JuniorFeeDefault.
This way the tests verify dispatch logic (0/1/2+ branching, map vs. fallback)
without breaking when rates are intentionally updated in the map.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Instead of hiding older months entirely, show all months in the from/to
selectors but default the from-select to the last MONTHS_TO_SHOW months
on page load. The "All" button resets to full history as before.
Python: passes months_to_show to render_template, IIFE sets fromSelect.value.
Go: adds MonthsToShow to response structs, data-months-to-show attr in
templates, filters.js reads it and defaults fromSelect after hideFutureMonths.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Show only the last MONTHS_TO_SHOW months (default 5) in the fee table columns
so the page fits on screen without horizontal scrolling. Reconciliation still
runs over the full month history so balances, credits, and debts are unaffected.
Set MONTHS_TO_SHOW=0 to show all months. Implemented in both Python and Go.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Juniors with exactly 1 session get expected='?' (manual-review marker
from attendance.py). The fill-first allocation block summed and cast
expected values numerically without guarding against this, causing a
TypeError: unsupported operand type(s) for +: 'int' and 'str' on the
/juniors route whenever any matched payment landed on such a month.
Add _expected_amount() helper that coerces non-numeric markers to 0
(same convention the final-balance calculation at line 512 already used)
and apply it in the two failing spots plus the existing isinstance check.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 2026-05: 700 → 450 CZK
- 2026-06, 07, 08: 600 CZK (new months)
Changes are mirrored in both Python (scripts/attendance.py) and Go (go/internal/domain/fees/fees.go).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
Replace proportional split with a fill-first loop that allocates
min(remaining, deficit) to each matched month in user-supplied order,
where deficit = expected - already_paid. Prior transactions' contributions
are now properly accounted for, so a second payment on overlapping months
fills only what's still owed instead of splitting proportionally by total
expected. Surplus after all deficits are covered goes to the credit bucket.
Fixes: Matyáš Thér 200+550 showing 566/183 instead of 500/250.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Fio's transparent page now serves recent rows as DD.MM.YY while older
rows stay DD.MM.YYYY. parse_czech_date only knew the 4-digit form so
every recent transfer was silently dropped — make sync-2026 reported
zero new transactions. Adds %d.%m.%y and %d/%m/%y to the format list,
mirroring the Go-side fix from 2026-05-07.
Also adds a Python analog of make go-sync-debug:
- --dry-run skips header write / append / sort and prints "would …" lines
- --print-fio-table prints aligned per-txn table with NEW/DUP status
- make sync-debug [DAYS=N] wrapper (default DAYS=30)
- always-on stderr diagnostics in fio_utils: which fetcher was chosen
(with FIO_API_TOKEN-unset lag warning) + raw-vs-filtered counts, so
this class of "scraper drops everything" bug surfaces immediately.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add TestEmbedCompleteness and TestStaticAssetsServed in
go/internal/web/assets_test.go. The completeness guard walks the
on-disk templates/ and static/ directories and asserts every file is
present in the corresponding embed.FS, catching forgotten files on
future additions. The static mux test hits /static/css/app.css and all
JS files through the same http.FileServerFS wiring used in server.go,
confirming assets are served from the embedded FS with correct
Content-Type and a 404 for unknown paths.
Standalone binary smoke test passed manually: binary copied to /tmp
(no adjacent templates/ or static/), assets served correctly.
Closes M6.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace bare <a href=/qr> Pay buttons with <button data-*> elements that
open an in-page #qrModal (matching Python's showPayQR UX), driven by a
new payment-qr.js vanilla-JS IIFE module. Remove the now-dead qrHref /
qrHrefAll template helpers from render.go.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds static/js/member-detail.js: fetches /api/<page> once on page load,
caches the response, and renders a per-member detail modal on [i] row click.
Keyboard nav: Esc closes, ↑/↓ walk visible (filtered) rows. All modal CSS
was already in place from M6.1.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Extract AssemblePayments(ctx) from ServePayments in api/handler.go,
mirroring the AssembleAdults/AssembleJuniors pattern
- Add PaymentsPageData view-model wrapper in render.go
- Rewire html_handler.go ServePayments to call AssemblePayments and
render with PaymentsPageData
- Replace payments.tmpl placeholder with real grouped-by-person ledger:
alphabetical member blocks, txn-table (Date/Amount/Purpose/Message),
newest-first rows, Unmatched/Unknown bucket
- Append ledger CSS classes to app.css (.ledger-container, .member-block,
.txn-table, .txn-date/amount/purpose/message, tr:hover)
- Add TestPaymentsPage markup test
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Extract AssembleJuniors(ctx) from ServeJuniors JSON handler so HTML
and JSON share the same view-model path (mirrors AssembleAdults pattern)
- Add JuniorsPageData wrapper in render.go
- Wire HTMLHandler.ServeJuniors to AssembleJuniors + render template
- Replace 4-line placeholder juniors.tmpl with full template:
member table, name filter, month-range filter, totals row,
Credits + Debts sections, Pay / Pay All buttons via /qr links
(no Unmatched section — matches Python juniors.html parity)
- J/A attendance breakdown ("3/500 CZK (4:2J,1A)") and "?" sentinel
rendered via MonthCell.Text from buildJuniorMemberRow, no extra
template logic needed
- All tests pass; make parity reports 3/3 routes OK
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two regressions made older periods invisible on the adults dashboard:
- 1257f0d (Mar 9) commented out ADULT_MERGED_MONTHS, removing the
Sep+Oct 2025 merged label. Restored only the 2025-09 → 2025-10
mapping (Dec and Jan are billed separately for adults; the
Dec → Jan mapping stays disabled per product decision). Mirrored
on the Go side. Test fixtures in sources_test.go now assert Sep
dates land in merged 2025-10 instead of 2025-09.
- 7774301 (Apr 9) added a JS onload default that set the From
selector to maxMonthIdx − 4 and immediately filtered the table,
hiding everything older than 5 months on first load. Dropped that
default in templates/adults.html and templates/juniors.html so
the From-selector starts at the oldest available month. Future
months are still removed from the dropdowns and hidden in the
table — only the past-month truncation is gone.
Note: the live adults attendance sheet had also been pruned to
start at 02.12.2025; restoring Sep/Oct/Nov 2025 columns from
Sheets version history is required to actually see those periods.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Map unpaid|partial → cell-unpaid (or cell-unpaid-current for current
month) and surplus → cell-overridden, matching Python's Jinja logic;
avoids emitting non-existent cell-partial/cell-surplus classes that
caused Pay buttons to escape the table.
- Add text-decoration: none to .pay-btn so anchor-based Pay links don't
show the default underline.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The lifted CSS defines .table-container with border: 1px solid #333 and
max-width: 1200px — without the wrapper the table stretched to full
viewport width and showed no border. Mirrors templates/adults.html.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Extract AssembleAdults(ctx) from ServeAdults so HTML and JSON API share one reconcile path.
- HTMLHandler gains *api.Handler; ServeAdults loads real data and renders adults.tmpl.
- AdultsPageData view model + qrHref/qrHrefAll funcMap (URL-encode /qr params, YYYY-MM→MM/YYYY).
- adults.tmpl: full reconcile table, per-cell status classes + cell-unpaid-current, Pay button hrefs,
totals row, credits/debts/unmatched sections, filter controls, sheet links.
- static/js/filters.js: NFD-normalize name filter + month-range column hiding; future months hidden by default.
- TestAdultsPage asserts member name and cell text against fixture data.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>