Documentation
¶
Index ¶
- func ApplyRules(t model.Transaction, mappings []model.Mapping, roles []model.AccountRole, ...) (model.FOSType, model.Confidence)
- func DetectInternalTransfers(txns []model.Transaction, database *db.DB) (int, error)
- func MerchantKey(description string) string
- func MerchantMatches(key, description string) bool
- type Engine
- type Resolution
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ApplyRules ¶
func ApplyRules( t model.Transaction, mappings []model.Mapping, roles []model.AccountRole, cfg config.Config, ) (model.FOSType, model.Confidence)
ApplyRules classifies a transaction using the priority-ordered rule set. Returns (FOSUnknown, ConfidenceNone) if no rule matches.
Priority order (highest to lowest):
- Account role = investment AND confirmed → excluded
- Account role = home_loan or bad_debt_loan AND confirmed → excluded
- txn_type mapping match
- Tag mapping match (by priority)
- Description glob match
- Merchant token match (normalized description tokens)
- Account name mapping match
- Amount ≥ review threshold → review
- → unclassified
Note: "passthrough" is NOT an account-level role. Pass-through transactions (e.g. NDIS flows, govt payments) are excluded via tag mappings (Social/NDIS, Social/CentreLink, etc.) because a single account can contain a mix of passthrough and personal transactions.
func DetectInternalTransfers ¶
DetectInternalTransfers finds Transfer-type transactions that are paired across different accounts (same absolute amount, opposite sign, within the settlement window) and marks both sides as excluded.
External transfers — payments to third-party accounts that have no matching counterpart in the user's own data — are left unclassified so the tag-based classifier can handle them.
The algorithm relies on the fact that every account in a Buxfer export belongs to the user: an internal transfer produces two rows (debit + credit) while an external transfer produces only one.
func MerchantKey ¶
MerchantKey derives a stable merchant identity from a raw bank description, suitable as the match_value of a "merchant" mapping. Only the part before the first comma is used (the remainder is card/memo boilerplate that varies per transaction), and the key is capped at the first 4 identity tokens. Returns "" when the description yields fewer than 2 tokens — too generic to make a safe rule (e.g. bare "Transfer").
func MerchantMatches ¶
MerchantMatches reports whether every token of a merchant key appears in the transaction description's identity tokens. Exported so that tooling auditing the rule set matches exactly what the engine matches, rather than reimplementing it and drifting.
Passing another merchant key as description answers "does key subsume it?", which is how overlapping rules are detected.
Types ¶
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine runs the classification pipeline against the database.
func (*Engine) ClassifyAll ¶
func (e *Engine) ClassifyAll() (model.ClassifyResult, error)
ClassifyAll runs a batch classification pass over all unclassified transactions.
Step 1 — DetectInternalTransfers: pairs Transfer-type transactions across own accounts and marks them excluded. External transfers (no matching counterpart) are left for step 2.
Step 2 — ApplyRules: tag/description/role-based rules classify everything else.
func (*Engine) InteractiveResolve ¶
InteractiveResolve prompts the user to classify all transactions that need attention: truly unclassified (fos_type IS NULL) and auto-flagged review items, in one pass. Returns the number of transactions that were resolved.
func (*Engine) InteractiveResolveList ¶
func (e *Engine) InteractiveResolveList(txns []model.Transaction) (int, error)
InteractiveResolveList prompts the user to classify the given transactions.
func (*Engine) PromoteMerchantRules ¶
func (e *Engine) PromoteMerchantRules(minCount int, eligible func(model.Transaction) bool) (int, error)
PromoteMerchantRules turns repeated identical classifications into rules: when the same merchant (see MerchantKey) has been classified as the same concrete FOS type at least minCount times in the recent window — and no existing rule already covers it — a "merchant" mapping is created with source "auto". Merchants with conflicting classifications are skipped. Returns the number of rules created.
eligible, when non-nil, restricts which transactions may teach a rule. Callers whose data source changed mid-history use it to stop the old era minting rules: the same payee described two ways yields two different merchant keys, so the conflict check below cannot see across the boundary and a stale classification can silently outlive its era.
type Resolution ¶
type Resolution struct {
TxnID string
FOSType model.FOSType
SaveMapping *model.Mapping // non-nil if the user wants to remember this rule
}
Resolution is the user's decision for a single transaction.
func RunInteractive ¶
func RunInteractive(txns []model.Transaction) ([]Resolution, error)
RunInteractive presents a huh form for each unclassified transaction.