classify

package
v0.0.0-...-c38abf2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 20, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Index

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):

  1. Account role = investment AND confirmed → excluded
  2. Account role = home_loan or bad_debt_loan AND confirmed → excluded
  3. txn_type mapping match
  4. Tag mapping match (by priority)
  5. Description glob match
  6. Merchant token match (normalized description tokens)
  7. Account name mapping match
  8. Amount ≥ review threshold → review
  9. → 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

func DetectInternalTransfers(txns []model.Transaction, database *db.DB) (int, error)

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

func MerchantKey(description string) string

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

func MerchantMatches(key, description string) bool

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 NewEngine

func NewEngine(database *db.DB, cfg config.Config) *Engine

NewEngine creates a classification engine.

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

func (e *Engine) InteractiveResolve() (int, error)

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL