Files
accounted/lib/company/__tests__/snapshot-bank.test.ts
T
MattssonandClaude Fable 5 1bb423b2b3 fix(salary): surface missing sender bankgiro/IBAN before betalfil download (#1640)
* fix(salary): surface missing sender bankgiro/IBAN before betalfil download

Users see a bankgiro under BANKUPPGIFTER in settings (Bolagsverket
snapshot, display only) while the payment-file routes read
company_settings.bankgiro, so the LB download failed with an error
that pointed at a page that looked correct. 153 companies have a
registry bankgiro but an empty settings field.

- PaymentFilePanel warns up front when the sender bankgiro (bg_lb)
  or IBAN (pain001) is missing, linking to Installningar -> Fakturering
- betalkonton form offers a one-click prefill of the bankgiro from
  companies.tic_snapshot (Luhn-validated, user still saves)
- bg-lb and skattekonto payment-file error copy now names the exact
  place to fix instead of 'foretagsinstallningar'

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(salary): harden bankgiro prefill and warning per skeptic review

- bankgiroFromTicSnapshot now requires the snapshot's orgNumber to match
  companies.org_number before suggesting anything: stale fuzzy-matched
  snapshots can hold another entity's profile, and this field becomes the
  payee account on invoices and Peppol e-invoices
- salary run page refetches settings when the URL returns from the
  intercepting settings modal, so a bankgiro/IBAN saved there clears the
  missing-sender warning instead of leaving it stale

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-17 11:15:37 +02:00

112 lines
3.8 KiB
TypeScript

import { describe, it, expect } from 'vitest'
import { bankgiroFromTicSnapshot } from '../snapshot-bank'
// 5402-9681 is a Luhn-valid synthetic bankgiro; 5402-9682 fails the check.
const VALID_BG = '54029681'
const INVALID_BG = '54029682'
const ORG = '5566778899'
function snapshot(overrides: Record<string, unknown> = {}) {
return {
orgNumber: ORG,
bankAccounts: [{ type: 'bankgiro', accountNumber: VALID_BG }],
...overrides,
}
}
describe('bankgiroFromTicSnapshot', () => {
it('returns null for null, undefined and non-object snapshots', () => {
expect(bankgiroFromTicSnapshot(null, ORG)).toBeNull()
expect(bankgiroFromTicSnapshot(undefined, ORG)).toBeNull()
expect(bankgiroFromTicSnapshot('a string', ORG)).toBeNull()
})
it('returns null when bankAccounts is missing or not an array', () => {
expect(bankgiroFromTicSnapshot(snapshot({ bankAccounts: undefined }), ORG)).toBeNull()
expect(bankgiroFromTicSnapshot(snapshot({ bankAccounts: 'nope' }), ORG)).toBeNull()
expect(bankgiroFromTicSnapshot(snapshot({ bankAccounts: null }), ORG)).toBeNull()
})
it('returns null when only non-bankgiro accounts exist', () => {
expect(
bankgiroFromTicSnapshot(
snapshot({ bankAccounts: [{ type: 'plusgiro', accountNumber: '1234567' }] }),
ORG,
),
).toBeNull()
})
it('returns the digits of a valid bankgiro account', () => {
expect(bankgiroFromTicSnapshot(snapshot(), ORG)).toBe(VALID_BG)
})
it('strips hyphens and spaces from the registry value', () => {
expect(
bankgiroFromTicSnapshot(
snapshot({ bankAccounts: [{ type: 'bankgiro', accountNumber: '5402-9681' }] }),
ORG,
),
).toBe(VALID_BG)
})
it('skips bankgiro entries that fail the Luhn check', () => {
expect(
bankgiroFromTicSnapshot(
snapshot({ bankAccounts: [{ type: 'bankgiro', accountNumber: INVALID_BG }] }),
ORG,
),
).toBeNull()
})
it('skips malformed entries and finds a later valid one', () => {
expect(
bankgiroFromTicSnapshot(
snapshot({
bankAccounts: [
null,
{ type: 'bankgiro' },
{ type: 'bankgiro', accountNumber: 12345 },
{ type: 'bankgiro', accountNumber: VALID_BG },
],
}),
ORG,
),
).toBe(VALID_BG)
})
describe('snapshot identity guard', () => {
it('returns null when the snapshot describes a different org', () => {
expect(bankgiroFromTicSnapshot(snapshot({ orgNumber: '5511223344' }), ORG)).toBeNull()
})
it('returns null when the snapshot has no orgNumber to prove identity', () => {
expect(bankgiroFromTicSnapshot(snapshot({ orgNumber: undefined }), ORG)).toBeNull()
expect(bankgiroFromTicSnapshot(snapshot({ orgNumber: 5566778899 }), ORG)).toBeNull()
})
it('returns null when the company org number is missing or empty', () => {
expect(bankgiroFromTicSnapshot(snapshot(), null)).toBeNull()
expect(bankgiroFromTicSnapshot(snapshot(), undefined)).toBeNull()
expect(bankgiroFromTicSnapshot(snapshot(), '')).toBeNull()
})
it('matches org numbers regardless of hyphenation', () => {
expect(bankgiroFromTicSnapshot(snapshot({ orgNumber: '556677-8899' }), ORG)).toBe(VALID_BG)
expect(bankgiroFromTicSnapshot(snapshot(), '556677-8899')).toBe(VALID_BG)
})
it('matches a 12-digit personnummer against its 10-digit form', () => {
expect(
bankgiroFromTicSnapshot(snapshot({ orgNumber: '198012311234' }), '801231-1234'),
).toBe(VALID_BG)
expect(
bankgiroFromTicSnapshot(snapshot({ orgNumber: '8012311234' }), '19801231-1234'),
).toBe(VALID_BG)
})
it('does not match on partial digit overlap', () => {
expect(bankgiroFromTicSnapshot(snapshot({ orgNumber: '66778899' }), ORG)).toBeNull()
})
})
})