export interface Condition { field: | "merchant_normalized" | "description" | "category" | "bank_name" | "amount" | "transaction_type" | "tag"; operator: "contains" | "equals" | "starts_with" | "gt" | "lt" | "not_equals"; value: string; } export interface SplitEntry { participant_id: number; share_percent: number; } export interface Actions { set_category?: string; add_tag_ids?: number[]; set_merchant?: string; apply_split?: SplitEntry[]; } export interface TxFields { effective_category: string; effective_merchant: string; description: string; bank_name: string; amount: number; transaction_type: string; tags: { id: number }[]; } export function evaluateCondition(cond: Condition, tx: TxFields): boolean { if (cond.field === "amount") { const numVal = Number(tx.amount); const numCond = Number(cond.value); switch (cond.operator) { case "equals": return numVal === numCond; case "not_equals": return numVal !== numCond; case "gt": return numVal > numCond; case "lt": return numVal < numCond; default: return false; } } if (cond.field === "tag") { const tagId = Number(cond.value); const hasTag = tx.tags.some((t) => t.id === tagId); return cond.operator === "not_equals" ? !hasTag : hasTag; } let fieldVal: string; switch (cond.field) { case "merchant_normalized": fieldVal = tx.effective_merchant || ""; break; case "description": fieldVal = tx.description || ""; break; case "category": fieldVal = tx.effective_category || ""; break; case "bank_name": fieldVal = tx.bank_name || ""; break; case "transaction_type": fieldVal = tx.transaction_type || ""; break; default: return false; } const strVal = fieldVal.toLowerCase(); const strCond = cond.value.toLowerCase(); switch (cond.operator) { case "contains": return strVal.includes(strCond); case "equals": return strVal === strCond; case "starts_with": return strVal.startsWith(strCond); case "not_equals": return strVal !== strCond; default: return false; } }