New go/internal/domain/matching package porting three helpers from scripts/match_payments.py: - BuildNameVariants: normalized ASCII variants from a member name (nickname in parens, last/first split, len<3 filtered); variants[0] is always the full base name — MatchMembers relies on this invariant. - MatchMembers: auto/review confidence matching with an exact-name short-circuit pass that prevents nickname substrings (tov) from firing inside longer surnames (ottova); common-surname filter for review tier. - FormatDate: nil/empty/""/serial int/float64 (since 1899-12-30, fractional days supported)/YYYY-MM-DD passthrough/garbage → never errors. - InferTransactionDetails: composes BuildNameVariants+MatchMembers+ ParseMonthReferences; falls back to sender-only member match and date-derived month when text carries no signal. 21 table-driven tests; all expected values verified against live Python on 2026-05-06. go-build, go-test, go-lint all clean. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
42 lines
1.1 KiB
Go
42 lines
1.1 KiB
Go
package matching
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
var sheetsEpoch = time.Date(1899, 12, 30, 0, 0, 0, 0, time.UTC)
|
|
|
|
// FormatDate normalizes a date value from Google Sheets.
|
|
//
|
|
// Accepts nil, empty string, int/float64 Sheets serial days since 1899-12-30,
|
|
// a pre-formatted "YYYY-MM-DD" string (returned as-is), or any other value
|
|
// (returned as fmt.Sprint(v).TrimSpace). Never returns an error.
|
|
//
|
|
// Ports scripts/match_payments.py format_date.
|
|
func FormatDate(val any) string {
|
|
if val == nil {
|
|
return ""
|
|
}
|
|
switch v := val.(type) {
|
|
case int:
|
|
return sheetsEpoch.Add(time.Duration(float64(v) * 24 * float64(time.Hour))).Format("2006-01-02")
|
|
case int64:
|
|
return sheetsEpoch.Add(time.Duration(float64(v) * 24 * float64(time.Hour))).Format("2006-01-02")
|
|
case float64:
|
|
return sheetsEpoch.Add(time.Duration(v * 24 * float64(time.Hour))).Format("2006-01-02")
|
|
case string:
|
|
s := strings.TrimSpace(v)
|
|
if s == "" {
|
|
return ""
|
|
}
|
|
if len(s) == 10 && s[4] == '-' && s[7] == '-' {
|
|
return s
|
|
}
|
|
return s
|
|
default:
|
|
return strings.TrimSpace(fmt.Sprint(v))
|
|
}
|
|
}
|