feat(infer): messages starting with "other:" are not monthly fees #49

Merged
kacerr merged 2 commits from feat/infer-other-purpose into main 2026-10-08 12:40:29 +02:00
Owner

What

When the bank message for the recipient starts with other:, inference no longer tries to find a month. It copies the message into Purpose, so the payment is linked to the person as an "other" transaction and is not counted as a monthly fee.

Reconcile already treated a Purpose starting with other: this way; until now that only happened when someone typed the purpose by hand.

Behaviour

  • Trigger: the message, trimmed, starts with other: (case-insensitive). A message that contains other: later in the text is inferred the old way.
  • Purpose: the trimmed message, unchanged. No month parsing and no fallback to the payment date.
  • Person: inferred as before.
  • No person found: Purpose is still written and the row appears under unmatched for a manual fix.
  • Rows not yet processed by inference follow the same rule inside reconcile.
  • Existing rows are not re-inferred. To reprocess one, clear its Person and Purpose cells.

Implemented in both Go and legacy Python.

Also in this MR

  • fix(go): the junior cell tooltip showed Expected: 0 instead of Expected: ? when a month with an unknown expected fee had a payment. Found because it made make parity fail on /api/juniors with current data. No unit test added for this one; it is covered by the live parity check only.

Testing

  • make go-test-all and make test (44 Python tests) pass.
  • Four new parity fixtures (three for inference, one for the reconcile fallback); existing fixtures are unchanged.
  • Dry-run inference on the live sheet in both implementations proposes no changes.
  • Live parity between the two servers: 3/3 routes match.

Note on live data

The payments sheet has one row from 2026-10-08 whose message starts with other:. It was inferred before this change and is booked against 2026-10. It needs a manual correction (or clearing Person and Purpose so it is re-inferred).

🤖 Generated with Claude Code

## What When the bank message for the recipient starts with `other:`, inference no longer tries to find a month. It copies the message into Purpose, so the payment is linked to the person as an "other" transaction and is not counted as a monthly fee. Reconcile already treated a Purpose starting with `other:` this way; until now that only happened when someone typed the purpose by hand. ## Behaviour - Trigger: the message, trimmed, starts with `other:` (case-insensitive). A message that contains `other:` later in the text is inferred the old way. - Purpose: the trimmed message, unchanged. No month parsing and no fallback to the payment date. - Person: inferred as before. - No person found: Purpose is still written and the row appears under unmatched for a manual fix. - Rows not yet processed by inference follow the same rule inside reconcile. - Existing rows are not re-inferred. To reprocess one, clear its Person and Purpose cells. Implemented in both Go and legacy Python. ## Also in this MR - `fix(go)`: the junior cell tooltip showed `Expected: 0` instead of `Expected: ?` when a month with an unknown expected fee had a payment. Found because it made `make parity` fail on `/api/juniors` with current data. No unit test added for this one; it is covered by the live parity check only. ## Testing - `make go-test-all` and `make test` (44 Python tests) pass. - Four new parity fixtures (three for inference, one for the reconcile fallback); existing fixtures are unchanged. - Dry-run inference on the live sheet in both implementations proposes no changes. - Live parity between the two servers: 3/3 routes match. ## Note on live data The payments sheet has one row from 2026-10-08 whose message starts with `other:`. It was inferred before this change and is booked against 2026-10. It needs a manual correction (or clearing Person and Purpose so it is re-inferred). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
kacerr added 2 commits 2026-10-08 12:37:05 +02:00
A junior month with one session has an unknown expected fee. When such
a month had a payment, the Go tooltip printed "Expected: 0" where
Python prints "Expected: ?", which made make parity fail on
/api/juniors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When the bank message starts with "other:" (case-insensitive),
inference does not look for a month, neither in the text nor from the
payment date. The trimmed message is written to Purpose, so reconcile
books the payment as an "other" transaction for the person instead of
a monthly fee. Messages containing "other:" elsewhere are unchanged.

- matching.IsOtherPurpose / is_other_purpose shared by inference and
  reconcile
- reconcile applies the rule to rows not yet processed by inference
- no person found: Purpose is still written, row shows as unmatched
- implemented in Go and legacy Python; four new parity fixtures

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kacerr merged commit 7d55eca189 into main 2026-10-08 12:40:29 +02:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: kacerr/fuj-management#49