perf(forms): supplier-invoice form, register forms and review dialogs read the session cache (#1938)
The supplier-invoice editor issued four requests on every mount (suppliers, accounts, settings, fiscal periods) and defaulted vatRegistered=true, entity type and rounding until /api/settings landed, so the moms controls visibly flipped. The register forms fetched the whole chart of accounts to fill one konto combobox, and each transaction review dialog refetched accounts, cash accounts or settings per open. - use-supplier-invoice-data: thin composition of useSuppliers, useAccounts, useCompanySettings and useFiscalPeriods; the settings-driven gates come from a pure deriveSupplierInvoiceDefaults() (tested) instead of state that flips when the fetch returns; the per-invoice öresavrundning toggle is the one local override. Inline supplier create invalidates the shared list instead of patching local state. - SupplierForm, ArticleForm (posting accounts), QuickReviewDialog, InvoiceMatchDialog, supplier-invoices/[id] (payment dialog chart): useAccounts; ArticleForm's inline account create invalidates the chart. - BulkBookDialog, MatchVoucherDialog, DuplicateBookingDialog: cash accounts from useCashAccounts (resolveAccount over the cached list; an empty list still resolves to 1930 with the fallback note). - QuickReviewDialog, BulkBookDialog, NewEmployeeDialog, customers list (default payment terms), salary run page (payment format, bank, IBAN, dimensions): derived from useCompanySettings; the salary page's post-settings-modal refetch becomes a cache invalidation. raw-reference-fetch ratchet: 45 -> 35 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:
co-authored by
Jakob Wennberg
Claude Fable 5
parent
40e773548c
commit
4560ccbfc9
@@ -1,6 +1,8 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { useAccounts } from '@/lib/reference-data/hooks'
|
||||
import { invalidateReferenceData } from '@/lib/reference-data/invalidate'
|
||||
import { useForm, Controller } from 'react-hook-form'
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { z } from 'zod'
|
||||
@@ -21,7 +23,7 @@ import { useCompany } from '@/contexts/CompanyContext'
|
||||
import { createClient } from '@/lib/supabase/client'
|
||||
import AccountCombobox from '@/components/bookkeeping/AccountCombobox'
|
||||
import { AddAccountDialog } from '@/components/bookkeeping/AddAccountDialog'
|
||||
import type { BASAccount, CreateArticleInput } from '@/types'
|
||||
import type { CreateArticleInput } from '@/types'
|
||||
import { INVOICE_POSTING_ACCOUNT_REGEX } from '@/lib/invoices/posting-account'
|
||||
import {
|
||||
ROT_WORK_TYPES,
|
||||
@@ -78,7 +80,13 @@ export default function ArticleForm({
|
||||
// unknown 4-digit numbers optimistically: the API answers with
|
||||
// ACCOUNTS_NOT_IN_CHART for activatable BAS accounts, and the host page's
|
||||
// ActivateAccountsDialog flow takes over (same UX as the journal entry form).
|
||||
const [postingAccounts, setPostingAccounts] = useState<BASAccount[]>([])
|
||||
// Balance-sheet and revenue accounts for the posting override, from the
|
||||
// session cache (lib/reference-data): populated on the first paint.
|
||||
const { accounts: activeAccounts } = useAccounts()
|
||||
const postingAccounts = useMemo(
|
||||
() => activeAccounts.filter((account) => account.account_class >= 1 && account.account_class <= 3),
|
||||
[activeAccounts],
|
||||
)
|
||||
// Inline account creation: what the user typed in the combobox when they hit
|
||||
// "Skapa konto": non-null opens AddAccountDialog prefilled with it.
|
||||
const [createAccountPrefill, setCreateAccountPrefill] = useState<string | null>(null)
|
||||
@@ -90,22 +98,6 @@ export default function ArticleForm({
|
||||
// fetch fails so the Select is never empty.
|
||||
const [currencies, setCurrencies] = useState<CurrencyOption[]>([])
|
||||
|
||||
async function fetchRevenueAccounts() {
|
||||
try {
|
||||
const res = await fetch('/api/bookkeeping/accounts')
|
||||
const body = await res.json()
|
||||
const accounts = ((body?.data as BASAccount[]) || [])
|
||||
.filter((account) => account.account_class >= 1 && account.account_class <= 3)
|
||||
setPostingAccounts(accounts)
|
||||
} catch {
|
||||
// Non-fatal: the combobox degrades to free 4-digit entry.
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
fetchRevenueAccounts()
|
||||
}, [])
|
||||
|
||||
// Currency options come from the currencies reference table: one source of
|
||||
// truth, no hard-coded list.
|
||||
useEffect(() => {
|
||||
@@ -604,7 +596,8 @@ export default function ArticleForm({
|
||||
: undefined
|
||||
}
|
||||
onCreated={async (account) => {
|
||||
await fetchRevenueAccounts()
|
||||
// The new account must be in the shared chart before the field points at it.
|
||||
await invalidateReferenceData('ref:accounts')
|
||||
setValue('revenue_account', account.account_number, { shouldDirty: true })
|
||||
setCreateAccountPrefill(null)
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user