perf(reference-data): sweep the remaining raw reads onto the session cache, ratchet to 0 (#1941)

Final consumer migration of the responsiveness plan: the 35 files still
fetching fiscal periods, settings, accounts, cash accounts, dimensions or
templates on their own now read lib/reference-data, and every client
write site invalidates the shared cache instead of refetching locally.

Settings and registries: FiscalYearsManager, FiscalPeriodEditor (period
snapshotted once per company so a revalidation cannot reset dates being
edited), BookingTemplatesPanel, ChartOfAccounts, ChartOfAccountsManager,
EditAccountDialog, CorrectionEntryDialog, StrikeLinesDialog,
InvoicePaymentAccountsSettings; the dimensions registry (DimensionsManager,
DimensionCombobox, LineDimensionFields, DimensionFilter, bookkeeping/[id])
reads useDimensions and the ad-hoc fetchDimensions/fetchDimensionsCached
helpers are deleted.

Pages and pickers: CashAccountSelector (FyPicker-shaped restore, once per
company load), use-account-names, FiscalYearGapNotice,
OpeningBalancePeriodStep, BankFileConfirmStep, ImportReviewStep, the import
page (invalidates accounts + periods after a SIE execute), customers list,
invoices list + detail, pending, salary employee, asset dispose, year-end
and periodisering pages (invalidate periods after closing), reports
DimensionPnlView (its pivot picker read the wrong payload key and was
always empty; it now populates), SkatteverketPanel, TemplatePicker,
ArticleForm (vat_registered).

Invoice dialogs and extensions: SendInvoiceDialog, PaymentBookingDialog
(init reduced to the credit-note lookup + catalogue, proposal and voucher
preview fire on open when cached; a local getSession replaces the network
getUser for the fallback CC), InvoiceInboxWorkspace, TicWorkspace,
ArcimMigrationWorkspace (invalidates after each SIE import step),
enable-banking AccountPickerDialog.

raw-reference-fetch ratchet: 35 -> 0 files.

Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-08-26 14:56:37 +02:00
committed by GitHub
co-authored by Jakob Wennberg Claude Fable 5
parent 4560ccbfc9
commit 3ee3565d6d
41 changed files with 636 additions and 977 deletions
@@ -23,6 +23,8 @@ import {
} from '@/components/bookkeeping/correction-entry-description'
import { nextLineDescriptionForAccountChange } from '@/components/bookkeeping/correction-line-description'
import { useToast } from '@/components/ui/use-toast'
import { useAccounts } from '@/lib/reference-data/hooks'
import { invalidateReferenceData } from '@/lib/reference-data/invalidate'
import { getErrorMessage } from '@/lib/errors/get-error-message'
import { Loader2, Plus, Trash2 } from 'lucide-react'
import { formatDate } from '@/lib/utils'
@@ -33,7 +35,7 @@ import {
} from '@/lib/bookkeeping/correction-line-account'
import { splitCreateAccountPrefill } from '@/lib/bookkeeping/create-account-prefill'
import { loadBasCatalog, type CatalogAccount } from '@/lib/bookkeeping/bas-catalog-client'
import type { JournalEntry, JournalEntryLine, BASAccount } from '@/types'
import type { JournalEntry, JournalEntryLine } from '@/types'
interface CorrectionLine {
account_number: string
@@ -53,9 +55,18 @@ export default function CorrectionEntryDialog({ entry, open, onOpenChange, onCor
const { toast } = useToast()
const router = useRouter()
const t = useTranslations('journal_detail')
const [accounts, setAccounts] = useState<BASAccount[]>([])
// The full chart (deactivated rows included) comes from the session cache
// (lib/reference-data); only the static BAS catalogue is loaded per open,
// and it is module-cached after the first time.
const { accounts, isLoading: accountsLoading, error: accountsError, refresh: refreshAccounts } = useAccounts(false)
const [catalog, setCatalog] = useState<CatalogAccount[]>([])
const [accountsStatus, setAccountsStatus] = useState<'loading' | 'ready' | 'error'>('loading')
const [catalogStatus, setCatalogStatus] = useState<'loading' | 'ready' | 'error'>('loading')
const accountsStatus: 'loading' | 'ready' | 'error' =
accountsLoading || catalogStatus === 'loading'
? 'loading'
: accountsError || catalogStatus === 'error'
? 'error'
: 'ready'
const [lines, setLines] = useState<CorrectionLine[]>([])
const [description, setDescription] = useState('')
const [isSubmitting, setIsSubmitting] = useState(false)
@@ -98,26 +109,18 @@ export default function CorrectionEntryDialog({ entry, open, onOpenChange, onCor
// Pre-fill the verifikationstext with the same auto text the server
// would generate; only a user edit is sent along (see handleSubmit).
setDescription(autoCorrectionDescription(entry.description))
void fetchAccounts()
void loadCatalog()
}
}, [open, entry.id]) // eslint-disable-line react-hooks/exhaustive-deps
async function fetchAccounts() {
setAccountsStatus('loading')
async function loadCatalog() {
setCatalogStatus('loading')
try {
const [res, basCatalog] = await Promise.all([
fetch('/api/bookkeeping/accounts?active=false'),
loadBasCatalog(),
])
if (!res.ok) throw new Error(`accounts ${res.status}`)
const { data } = await res.json()
setAccounts(data || [])
setCatalog(basCatalog)
setAccountsStatus('ready')
setCatalog(await loadBasCatalog())
setCatalogStatus('ready')
} catch {
setAccounts([])
setCatalog([])
setAccountsStatus('error')
setCatalogStatus('error')
}
}
@@ -169,9 +172,9 @@ export default function CorrectionEntryDialog({ entry, open, onOpenChange, onCor
// a dead end here: the rättelse can only post to accounts that exist in the
// chart. Creating it inline keeps the half-finished rättelse intact.
const handleAccountCreated = async (account: { account_number: string; account_name?: string }) => {
await fetchAccounts()
await invalidateReferenceData('ref:accounts')
if (creatingAccountForLine != null) {
// fetchAccounts' state update is not visible in this closure, so the
// The refreshed cache is not visible in this closure, so the
// fresh account's own name is passed alongside the stale sources. The
// reactivate path reports no name, but that account is already in
// `accounts` (the fetch includes deactivated rows).
@@ -339,7 +342,7 @@ export default function CorrectionEntryDialog({ entry, open, onOpenChange, onCor
{accountsStatus === 'loading' ? t('accounts_loading') : t('accounts_load_failed')}
</span>
{accountsStatus === 'error' && (
<Button variant="outline" size="sm" onClick={() => void fetchAccounts()}>
<Button variant="outline" size="sm" onClick={() => void refreshAccounts()}>
{t('accounts_retry')}
</Button>
)}