All checks were successful
Deploy to K8s / deploy (push) Successful in 6s
SHA-256 dedup hash from sync_fio_to_sheets.py generate_sync_id. Key subtlety: Python str(float) emits "500.0" for whole-valued floats and switches to scientific notation at |f|>=1e16 or |f|<1e-4 — replicated via formatAmount using 'f'/'e' format selection. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
71 lines
5.9 KiB
Markdown
71 lines
5.9 KiB
Markdown
# Changelog
|
||
|
||
## 2026-05-06 12:43 CEST — feat(go/M2.6): port domain/synch.GenerateSyncID
|
||
|
||
- New `go/internal/domain/synch` package with `GenerateSyncID(Transaction) string` ported from `scripts/sync_fio_to_sheets.py` `generate_sync_id`.
|
||
- Byte-stable SHA-256 hash over `date|amount|currency|sender|vs|message|bank_id` (lowercased, UTF-8); `Currency: ""` defaults to `"CZK"` matching the Python missing-key fallback.
|
||
- Key subtlety: Python's `str(float)` emits `"500.0"` for whole-valued floats and switches to scientific notation at `|f| >= 1e16` or `|f| < 1e-4` — replicated in `formatAmount` using `'f'`/`'e'` format selection.
|
||
- 6 table-driven hash tests + 9 `formatAmount` tests; all expected values verified against live Python on 2026-05-06.
|
||
|
||
## 2026-05-06 09:38 CEST — feat(go/M2.5): port domain/money.ParseCZK
|
||
|
||
- New `go/internal/domain/money` package with `ParseCZK(string) (float64, error)` ported from `scripts/infer_payments.py` `parse_czk_amount`.
|
||
- Preserves the Czech-locale heuristic: comma → decimal sep; 2+ dots → thousand seps; single dot → decimal (so `"1.500"` → `1.5`).
|
||
- Returns `(0, ErrInvalidAmount)` on parse failure; callers wanting Python's silent-zero contract use `v, _ := ParseCZK(s)`.
|
||
- 15 table-driven tests plus a silent-zero contract test; all expected values verified against live Python on 2026-05-06.
|
||
|
||
## 2026-05-06 09:24 CEST — feat(go/M2.3+M2.4): port domain/fees.CalculateFee and CalculateJuniorFee
|
||
|
||
- New `go/internal/domain/fees` package with adult and junior fee calculators ported from `scripts/attendance.py`.
|
||
- `CalculateFee(count, monthKey) int` — `0→0`, `1→200`, `2+→AdultFeeMonthlyRate[month]` (fallback 700 CZK).
|
||
- `CalculateJuniorFee(count, monthKey) Expected` — `0→{0}`, `1→{Unknown:true}` (the `"?"` sentinel, now strictly typed), `2+→JuniorFeeMonthlyRate[month]` (fallback 500 CZK).
|
||
- 20 table-driven tests, all verified against live Python; `-race` clean; `golangci-lint` clean.
|
||
|
||
## 2026-05-06 00:07 CEST — feat(go/M2.2): port czech.ParseMonthReferences
|
||
|
||
- `internal/domain/czech.ParseMonthReferences`: three-pass regex (numeric slash, dot, Czech month names) with range wrap-around and `m≥10 → previousYear` heuristic, byte-equivalent to Python.
|
||
- 35 table-driven tests; all expected outputs verified against live Python before locking (addresses risk #4 from the rewrite plan).
|
||
|
||
## 2026-05-05 23:33 CEST — feat(go/M2.1): port czech.Normalize
|
||
|
||
- First M2 pure-domain task: `internal/domain/czech.Normalize` (NFKD + Mn-strip + lowercase), byte-equivalent to Python `czech_utils.normalize`.
|
||
- Adds `golang.org/x/text v0.36.0` as first external Go dependency.
|
||
- 13-case table-driven test, all spot-checked against Python before locking.
|
||
|
||
## 2026-05-04 23:08 CEST — fix: payment inference exact-match short-circuit
|
||
|
||
- `match_members()` now short-circuits on whole-word full-name hits; nickname/partial checks only run when no full name is present.
|
||
- Replaced bare `in` substring checks with `_word_in()` word-boundary regex throughout, closing the class of bugs where a short nickname (e.g. `tov`) matches inside another member's surname (`ottova`).
|
||
- Added `tests/test_match_members.py` (6 cases). Affects `scripts/match_payments.py`.
|
||
|
||
## 2026-05-04 23:08 CEST — feat: lower adult monthly fee to 700 CZK from April 2026
|
||
|
||
- `ADULT_FEE_DEFAULT` reduced from 750 → 700 CZK.
|
||
- `ADULT_FEE_MONTHLY_RATE` now pins Sep 2025 – Feb 2026 at 750 to preserve historical billing; Mar 2026 stays 350; Apr–May 2026 at 700. Affects `scripts/attendance.py`.
|
||
|
||
## 2026-05-04 12:02 CEST — Go rewrite M1: skeleton + tooling
|
||
|
||
- Created `go/` tree with module `fuj-management/go` (Go 1.26).
|
||
- `cmd/fuj`: stdlib-flag subcommand dispatcher; `server` and `version` implemented, stubs for M2/M4 commands.
|
||
- `internal/config`: env loader mirroring `scripts/config.py` (same env var names and defaults).
|
||
- `internal/logging`: slog setup accepting log level from config.
|
||
- `internal/web`: `net/http` ServeMux on `:8080`; `middleware/timer.go` logs method/path/status/ms.
|
||
- `go/build/Dockerfile`: multi-stage (`golang:1.26` → `alpine:3`) producing a static binary image.
|
||
- Makefile: `web` → `web-py` alias; added `web-go`, `go-build`, `go-test`, `go-run`, `go-lint`.
|
||
- `.gitea/workflows/build.yaml`: parallel `build-go` job pushing `<tag>-go` image.
|
||
- Gate: `make go-build`, `make go-lint`, `make go-test`, `curl :8080` all pass.
|
||
|
||
## 2026-05-03 20:37 CEST — Fix Balance column to correctly reflect past-month debt
|
||
|
||
- Balance (and Pay-All) are now computed as `sum(paid − expected)` over past months only, iterating directly over the ledger entries from `reconcile()`.
|
||
- Previously the balance used `total_balance` (which includes current/future-month activity and out-of-window credits) plus a one-sided current-month debt adjustment. Current-month *surplus* leaked through, making the balance appear less negative than the actual past-month debt.
|
||
- Pay-All is now `max(0, −balance)` so the two values are derived from a single source and can never disagree.
|
||
- Affected: `adults_view()` and `juniors_view()` in `app.py`.
|
||
|
||
## 2026-05-03 19:26 CEST — Fee-aware allocation for multi-month payments
|
||
|
||
- `reconcile()` no longer splits a multi-month payment evenly. Allocation is now per-member with two phases: greedy (if amount ≥ total expected, each month gets exactly its expected fee and overflow → credit) and proportional (otherwise distribute by each month's expected). Fixes the case where e.g. 1250 CZK covering 3 months with mixed fees (750/350/150) marked two months red.
|
||
- Out-of-window months keep the previous even-split-to-credit behavior. Fallback to even split when all matched months have `expected = 0` (prepayment before attendance is recorded).
|
||
- Display layer only — no changes to how payments are stored in Google Sheets; `Inferred Amount` still holds the full bank amount.
|
||
- Files: [scripts/match_payments.py](scripts/match_payments.py), [tests/test_reconcile_exceptions.py](tests/test_reconcile_exceptions.py) (6 new test cases).
|