27383fd02b
* fix(inbox): surface full BAS catalog in BookDirectlyDialog account picker The account combobox in the inbox book-directly flow was fed only the company's active chart, so typing a prefix like 65 showed just the two activated 65xx accounts and a search for 6540 found nothing, which reads as the account not existing. Pass the cached BAS catalogue (same pattern as JournalEntryForm) so every standard account is searchable; picking a not-yet-activated account flows through the existing ActivateAccountsDialog rail at booking. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(bookkeeping): log BAS catalog fetch failures Compliance swarm finding (SOC 2 CC7.2): loadBasCatalog swallowed fetch errors silently, leaving catalog-load failures unobservable. Log inside the client's catch, which is the only place the error actually surfaces: callers' own .catch handlers are unreachable since the shared promise already resolves to an empty list on failure. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(bookkeeping): unit-cover loadBasCatalog fetch, fallback, and retry CodeRabbit finding on PR #1543: the catalog client had no focused coverage. Tests assert the success path with promise caching, empty-list fallback with logging on non-OK responses, missing data field handling, and cache clearing after a failure so the next call refetches. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
41 lines
1.3 KiB
TypeScript
41 lines
1.3 KiB
TypeScript
'use client'
|
|
|
|
import type { SearchableAccount } from '@/lib/bookkeeping/account-search'
|
|
|
|
/**
|
|
* Client-side loader for the full BAS catalogue used by AccountCombobox.
|
|
*
|
|
* The catalogue is static reference data, identical for every company, so we
|
|
* fetch it once per session and share the in-flight promise across every
|
|
* combobox instance and form mount. A failed fetch clears the cache so the
|
|
* next caller retries rather than being stuck with an empty list.
|
|
*/
|
|
export interface CatalogAccount extends SearchableAccount {
|
|
account_number: string
|
|
account_name: string
|
|
account_class: number
|
|
account_group: string
|
|
description: string | null
|
|
}
|
|
|
|
let cache: Promise<CatalogAccount[]> | null = null
|
|
|
|
export function loadBasCatalog(): Promise<CatalogAccount[]> {
|
|
if (!cache) {
|
|
cache = fetch('/api/bookkeeping/accounts/bas-catalog')
|
|
.then((res) => {
|
|
if (!res.ok) throw new Error(`bas-catalog ${res.status}`)
|
|
return res.json()
|
|
})
|
|
.then((body) => (body?.data as CatalogAccount[]) ?? [])
|
|
.catch((err) => {
|
|
// Callers degrade gracefully (search falls back to the active chart),
|
|
// so this log is the only trace a catalog fetch failure leaves.
|
|
console.error('[bas-catalog] fetch failed:', err)
|
|
cache = null // allow a retry on the next call
|
|
return []
|
|
})
|
|
}
|
|
return cache
|
|
}
|