money: tag interest/dividend payouts on bank sync and Revolut import (category interest|dividend, never overwrites a set category)
All checks were successful
Deploy production / deploy (push) Successful in 1m14s

This commit is contained in:
2026-10-26 07:48:47 +00:00
parent 0121071af2
commit d3b9bb9ba2
8 changed files with 171 additions and 5 deletions

View File

@@ -11,6 +11,7 @@ import type {
SessionResponse
} from './enable-banking';
import { EnableBankingError } from './enable-banking';
import { investmentIncomeCategory } from './investment-income';
import { findSumMatch } from './reconcile';
export type MappedTransaction = {
@@ -21,6 +22,8 @@ export type MappedTransaction = {
counterparty: string | null;
amountCents: number;
bookingStatus: string;
/** 'interest' / 'dividend' for investment income payouts, else null. */
category: string | null;
raw: BankTransaction;
};
@@ -79,6 +82,17 @@ export function mapBankTransaction(tx: BankTransaction): MappedTransaction | nul
counterparty,
amountCents,
bookingStatus: tx.status ?? 'BOOK',
category: investmentIncomeCategory({
amountCents,
kind: tx.bank_transaction_code?.code,
texts: [
description,
...remittance,
tx.bank_transaction_code?.description,
tx.bank_transaction_code?.sub_code,
tx.note
]
}),
raw: tx
};
}
@@ -435,10 +449,11 @@ export async function syncBankAccounts(
for (const { row } of absorbed)
await q`delete from money.transactions where id = ${row.id} and external_id is null`;
for (const { id, tx } of updates) {
// Keep Stan's own description/category; attach the bank identity.
// Keep Stan's own description/category (only fill an empty one); attach the bank identity.
await q`
update money.transactions set external_id = ${tx.externalId},
counterparty = coalesce(counterparty, ${tx.counterparty}),
category = coalesce(category, ${tx.category}),
booking_status = ${tx.bookingStatus}, value_date = ${tx.valueDate},
raw = ${q.json(tx.raw as never)}
where id = ${id} and external_id is null`;
@@ -447,10 +462,10 @@ export async function syncBankAccounts(
await q`
insert into money.transactions
(user_id, account_id, date, description, amount_cents, external_id, source,
booking_status, counterparty, raw, value_date)
booking_status, counterparty, category, raw, value_date)
values (${account.user_id}, ${account.id}, ${tx.date}, ${tx.description},
${tx.amountCents}, ${tx.externalId}, 'enablebanking', ${tx.bookingStatus},
${tx.counterparty}, ${q.json(tx.raw as never)}, ${tx.valueDate})
${tx.counterparty}, ${tx.category}, ${q.json(tx.raw as never)}, ${tx.valueDate})
on conflict (account_id, external_id) do nothing`;
}
});