Migrants arrive with vouchers, not bank transactions, so the bank-keyed ledger context is empty for them. This adds the description-keyed twin. - public.ledger_key(text): legibility key on top of the frozen normalize_counterparty_key mirror: strips AP-register prefixes (levfakt, leverantörsfaktura från N, levbet, faktura, kvitto, utgift), the supplier number that follows them, and trailing 1-3 digit runs, never "inköp". Mirrored by lib/parties/ledger-key.ts; the pair is pinned by a shared fixture list in the pg test. - public.get_observed_parties(company, from_date, limit): posted vouchers grouped by ledger_key(description) with occurrences, variants, expense and revenue SEK from the lines, first/last seen, median cadence and the Laplace-smoothed dominant result account. Excludes storno, opening balance, year-end and VAT settlement, and vouchers that carry a bank merchant name (those stay with get_ledger_deep_context). SECURITY INVOKER, so RLS scopes it. Never stored. - lib/parties/classify.ts: the deterministic pre-classifier moved out of the evaluation script so product and evaluation share one implementation (0.965 agreement with the founder labels, party recall 0.99). - lib/parties/observed.ts: RPC wrapper that classifies each row and derives a display rhythm from the cadence. Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
38 lines
1.5 KiB
TypeScript
38 lines
1.5 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import { ledgerKey } from '../ledger-key'
|
|
|
|
// Fixture pairs are shared with tests/pg/observed-parties-rpc.pg.test.ts,
|
|
// which runs the same inputs through public.ledger_key() and asserts parity.
|
|
export const LEDGER_KEY_CASES: [string, string][] = [
|
|
['Levfakt BEIJER BYGGMATERIAL AB (2089)', 'beijer byggmaterial'],
|
|
['Levfakt Beijer Byggmaterial AB, 097 (1001)', 'beijer byggmaterial'],
|
|
['Leverantörsfaktura från 18 Loopia, 1009146000', 'loopia'],
|
|
['Levfakt Varsego Sverige AB (178)', 'varsego sverige'],
|
|
['Levfkt 1555 Telge Energi', 'telge energi'],
|
|
['Levbet. MiSUMi (2189107)', 'misumi'],
|
|
['Kvitto OpenAI', 'openai'],
|
|
['Inköp av varor', 'inköp av varor'],
|
|
['Bankkostnad', 'bankkostnad'],
|
|
['Google Workspace - 2025-09', 'google workspace'],
|
|
['Telia Sverige AB', 'telia sverige'],
|
|
['', ''],
|
|
]
|
|
|
|
describe('ledgerKey', () => {
|
|
it.each(LEDGER_KEY_CASES)('%s -> %s', (raw, expected) => {
|
|
expect(ledgerKey(raw)).toBe(expected)
|
|
})
|
|
|
|
it('never strips "inköp", which would turn a category into a vendor', () => {
|
|
expect(ledgerKey('Inköp varor material')).toBe('inköp varor material')
|
|
})
|
|
|
|
it('falls back to the normalised key when stripping would leave nothing', () => {
|
|
expect(ledgerKey('Faktura 12')).not.toBe('')
|
|
})
|
|
|
|
it('merges the two AP spellings of one supplier onto one key', () => {
|
|
expect(ledgerKey('Levfakt BEIJER BYGGMATERIAL AB (2089)')).toBe(ledgerKey('Levfakt Beijer Byggmaterial AB, 097 (1001)'))
|
|
})
|
|
})
|