fix(transactions): resolve bank account from cash_account_id in booking dialog (#769)
* feat(transactions): expose cash_account_id in list API response Add cash_account_id to the transactions list API select so that components can resolve the bank account from the transaction instead of hardcoding. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Jonas Flodén <jonas@floden.nu> * feat(cash-accounts): extract resolveAccount to shared utility Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Jonas Flodén <jonas@floden.nu> * refactor(transactions): use shared resolveAccount in MatchVoucherDialog Replace the local resolveAccount function with the shared utility from lib/cash-accounts/resolve-account, reducing code duplication and improving maintainability. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Jonas Flodén <jonas@floden.nu> * fix(transactions): resolve bank account from cash_account_id in booking dialog Replaces the hardcoded '1930' bank leg in TransactionBookingDialog with the actual ledger_account of the transaction's cash account. Companies with multiple bank accounts (e.g. 1930 + 1940) now get the correct account pre-filled in both the blank and template-based booking flows. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Jonas Flodén <jonas@floden.nu> * fix(transactions): cancel stale cash-account fetch on dialog re-open Signed-off-by: Jonas Flodén <jonas@floden.nu> * fix(transactions): prevent form remount discarding edits during bank account fetch Hold JournalEntryForm render until the /api/cash-accounts fetch resolves by changing bankAccount state to string | null (null = pending). This prevents the form from mounting with key '…-1930', then immediately remounting with the correct account key and losing any user edits made in the sub-100ms window. Also adds r.ok guard before parsing and sets '1930' as explicit catch fallback. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Jonas Flodén <jonas@floden.nu> * fix(transactions): cancel stale cash-account fetch in MatchVoucherDialog Pass a signal object into loadCandidates and return a cleanup from the useEffect so a stale in-flight fetch (from a previous transaction) cannot call setAccountNumber/setAccountFallback/setGlLines/setSelected after the dialog re-opens for a different transaction. Also adds r.ok check before parsing /api/cash-accounts response. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Jonas Flodén <jonas@floden.nu> --------- Signed-off-by: Jonas Flodén <jonas@floden.nu>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
5b4cefe8ab
commit
1cd8863958
@@ -0,0 +1,72 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { resolveAccount } from '@/lib/cash-accounts/resolve-account'
|
||||
import type { CashAccount } from '@/types'
|
||||
|
||||
function makeCashAccount(overrides: Partial<CashAccount> = {}): CashAccount {
|
||||
return {
|
||||
id: 'ca-1',
|
||||
company_id: 'company-1',
|
||||
bank_connection_id: null,
|
||||
external_uid: null,
|
||||
iban: null,
|
||||
bg_pg: null,
|
||||
name: null,
|
||||
currency: 'SEK',
|
||||
ledger_account: '1930',
|
||||
balance: null,
|
||||
balance_updated_at: null,
|
||||
enabled: true,
|
||||
is_primary: true,
|
||||
source: 'manual',
|
||||
created_at: '2024-01-01T00:00:00Z',
|
||||
updated_at: '2024-01-01T00:00:00Z',
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
describe('resolveAccount', () => {
|
||||
it('returns the ledger_account for the bound cash account when cash_account_id matches', () => {
|
||||
const accounts = [
|
||||
makeCashAccount({ id: 'ca-1', ledger_account: '1930' }),
|
||||
makeCashAccount({ id: 'ca-2', ledger_account: '1940', is_primary: false }),
|
||||
]
|
||||
const result = resolveAccount(accounts, 'ca-2', 'SEK')
|
||||
expect(result).toEqual({ account: '1940', fallback: false })
|
||||
})
|
||||
|
||||
it('falls back to the sole enabled same-currency account when cash_account_id is null', () => {
|
||||
const accounts = [makeCashAccount({ id: 'ca-1', ledger_account: '1920', currency: 'EUR' })]
|
||||
const result = resolveAccount(accounts, null, 'EUR')
|
||||
expect(result).toEqual({ account: '1920', fallback: false })
|
||||
})
|
||||
|
||||
it('falls back to 1930 when cash_account_id is null and multiple same-currency accounts exist', () => {
|
||||
const accounts = [
|
||||
makeCashAccount({ id: 'ca-1', ledger_account: '1930', currency: 'SEK' }),
|
||||
makeCashAccount({ id: 'ca-2', ledger_account: '1940', currency: 'SEK', is_primary: false }),
|
||||
]
|
||||
const result = resolveAccount(accounts, null, 'SEK')
|
||||
expect(result).toEqual({ account: '1930', fallback: true })
|
||||
})
|
||||
|
||||
it('falls back to 1930 when cash_account_id does not match any account', () => {
|
||||
const accounts = [makeCashAccount({ id: 'ca-1', ledger_account: '1930' })]
|
||||
const result = resolveAccount(accounts, 'ca-unknown', 'SEK')
|
||||
expect(result).toEqual({ account: '1930', fallback: true })
|
||||
})
|
||||
|
||||
it('falls back to 1930 when accounts list is empty', () => {
|
||||
const result = resolveAccount([], null, 'SEK')
|
||||
expect(result).toEqual({ account: '1930', fallback: true })
|
||||
})
|
||||
|
||||
it('ignores disabled accounts in the currency fallback path', () => {
|
||||
const accounts = [
|
||||
makeCashAccount({ id: 'ca-1', ledger_account: '1930', enabled: true }),
|
||||
makeCashAccount({ id: 'ca-2', ledger_account: '1940', enabled: false, is_primary: false }),
|
||||
]
|
||||
// Only one enabled SEK account → resolves without fallback
|
||||
const result = resolveAccount(accounts, null, 'SEK')
|
||||
expect(result).toEqual({ account: '1930', fallback: false })
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user