feat(go/M2.10): port domain/reconcile.Reconcile #10

Merged
kacerr merged 2 commits from feat/m2-10-reconcile-domain into main 2026-05-06 16:55:17 +02:00
Owner

Ports scripts/match_payments.py reconcile() — the most load-bearing pure function in the codebase — to go/internal/domain/reconcile.

What's included

reconcile.go — types + Reconcile():

  • Input types: Member, FeeData, Transaction, Exception, ExceptionKey
  • Output types: MonthData, MemberResult, OtherEntry, TxEntry, Result
  • Three allocation phases, verbatim from Python:
    1. Greedy — payment ≥ total expected → fill each month exactly; overflow → credit
    2. Proportional — payment < total → distribute by each month's expected share; last month absorbs float remainder
    3. Even-split fallback — all expected = 0 (prepayment) → divide equally
  • canonicalMemberKey for diacritics/case/whitespace-tolerant Person-column resolution
  • [?] marker stripping from Person cells
  • other: purpose routing to OtherTransactions
  • Out-of-window month credit accumulation
  • Inference fallback via matching.InferTransactionDetails when Person/Purpose columns are empty

reconcile_test.go — 12 unit tests:

  • All cases from tests/test_reconcile_exceptions.py and tests/test_match_payments.py
  • Go-only extras: [?] stripping, other: purpose, out-of-window credit, inference fallback, no-match unmatched, empty-transaction guard

Test run

ok  fuj-management/go/internal/domain/reconcile  0.3s

make go-lint clean.

Ports `scripts/match_payments.py reconcile()` — the most load-bearing pure function in the codebase — to `go/internal/domain/reconcile`. ## What's included **`reconcile.go`** — types + `Reconcile()`: - Input types: `Member`, `FeeData`, `Transaction`, `Exception`, `ExceptionKey` - Output types: `MonthData`, `MemberResult`, `OtherEntry`, `TxEntry`, `Result` - Three allocation phases, verbatim from Python: 1. **Greedy** — payment ≥ total expected → fill each month exactly; overflow → credit 2. **Proportional** — payment < total → distribute by each month's expected share; last month absorbs float remainder 3. **Even-split fallback** — all expected = 0 (prepayment) → divide equally - `canonicalMemberKey` for diacritics/case/whitespace-tolerant Person-column resolution - `[?]` marker stripping from Person cells - `other:` purpose routing to `OtherTransactions` - Out-of-window month credit accumulation - Inference fallback via `matching.InferTransactionDetails` when Person/Purpose columns are empty **`reconcile_test.go`** — 12 unit tests: - All cases from `tests/test_reconcile_exceptions.py` and `tests/test_match_payments.py` - Go-only extras: `[?]` stripping, `other:` purpose, out-of-window credit, inference fallback, no-match unmatched, empty-transaction guard ## Test run ``` ok fuj-management/go/internal/domain/reconcile 0.3s ``` `make go-lint` clean.
kacerr added 2 commits 2026-05-06 16:16:52 +02:00
Three-phase payment allocation (greedy / proportional / even-split)
ported verbatim from scripts/match_payments.py reconcile().
Includes 12 unit tests covering all Python test cases plus Go-only
extras: [?] stripping, other: purpose, out-of-window credit, inference
fallback, and no-match/empty-transaction guards.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
chore: tick M2.10 in progress tracker + CHANGELOG entry
All checks were successful
Deploy to K8s / deploy (push) Successful in 9s
9e6aebc816
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
kacerr force-pushed feat/m2-10-reconcile-domain from 9e6aebc816 to 71278e6f7a 2026-05-06 16:54:37 +02:00 Compare
kacerr merged commit ea8622a541 into main 2026-05-06 16:55:17 +02:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: kacerr/fuj-management#10