feat(infer): messages starting with "other:" are not monthly fees
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>
This commit is contained in:
@@ -257,6 +257,33 @@ func TestReconcileQuestionMarkMarkerStripped(t *testing.T) {
|
||||
}
|
||||
|
||||
// [Go] Purpose "other:shirt" puts payment in OtherTransactions, not in month ledger.
|
||||
// A row not yet processed by inference (empty Person/Purpose) whose message
|
||||
// starts with "other:" must land in the other ledger, not in a month.
|
||||
func TestReconcileOtherMessageFallback(t *testing.T) {
|
||||
t.Parallel()
|
||||
members := []Member{{Name: "Alice", Tier: "A", Fees: map[string]FeeData{"2026-01": {Expected: 750, Attendance: 3}}}}
|
||||
txs := []Transaction{{
|
||||
Date: "2026-01-10", Amount: 300,
|
||||
Sender: "Alice", Message: "other: shirt",
|
||||
}}
|
||||
|
||||
result := Reconcile(members, []string{"2026-01"}, txs, nil, 2026)
|
||||
|
||||
alice := result.Members["Alice"]
|
||||
if paid := alice.Months["2026-01"].Paid; paid != 0 {
|
||||
t.Errorf("2026-01 paid = %v, want 0", paid)
|
||||
}
|
||||
if len(alice.OtherTransactions) != 1 {
|
||||
t.Fatalf("other transactions = %d, want 1", len(alice.OtherTransactions))
|
||||
}
|
||||
if got := alice.OtherTransactions[0]; got.Amount != 300 || got.Purpose != "other: shirt" {
|
||||
t.Errorf("other entry = %+v", got)
|
||||
}
|
||||
if len(result.Unmatched) != 0 {
|
||||
t.Errorf("unmatched = %d, want 0", len(result.Unmatched))
|
||||
}
|
||||
}
|
||||
|
||||
func TestReconcileOtherPurpose(t *testing.T) {
|
||||
t.Parallel()
|
||||
members := []Member{{Name: "Alice", Tier: "A", Fees: map[string]FeeData{"2026-01": {Expected: 750, Attendance: 3}}}}
|
||||
|
||||
Reference in New Issue
Block a user