* fix(bookkeeping): verifikat views show company account names for off-catalog accounts Verifikat views (entry detail, JournalEntryList, StrikeLinesDialog, CorrectionPreview) rendered AccountNumber without a name source, so accounts outside the BAS 2026 catalog (typically SIE-imported, e.g. a legacy 1580) showed a blank Benamning even though the company's kontoplan has the name. AccountNumber now falls back to a session-cached map of the company's own chart-of-accounts names (one fetch per page load, inactive accounts included for historical verifikat). Precedence: explicit name prop, then the company map, then the BAS reference name, keeping the PR #1968 rule that user-editable chart rows win over hardcoded copy. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N1rcRYRM8DyVp48mp5NNVc * refactor(bookkeeping): read company account names via the shared reference-data cache The check:guards raw-reference-fetch ratchet rightly rejected the hand-rolled session cache: lib/reference-data/hooks.ts already provides useAccounts with a per-company SWR cache seeded by the dashboard layout. AccountNumber now derives the company name from useAccounts(false) instead, and the bespoke hook and its tests are removed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N1rcRYRM8DyVp48mp5NNVc --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
134 lines
3.9 KiB
TypeScript
134 lines
3.9 KiB
TypeScript
'use client'
|
|
|
|
import { useMemo } from 'react'
|
|
import { getAccountDescription, type AccountType } from '@/lib/bookkeeping/account-descriptions'
|
|
import { useBasReference } from '@/lib/bookkeeping/use-bas-reference'
|
|
import { useAccounts } from '@/lib/reference-data/hooks'
|
|
import {
|
|
Tooltip,
|
|
TooltipTrigger,
|
|
TooltipContent,
|
|
} from '@/components/ui/info-tooltip'
|
|
import { cn } from '@/lib/utils'
|
|
|
|
const TYPE_COLORS: Record<AccountType, string> = {
|
|
asset: 'bg-success',
|
|
liability: 'bg-warning',
|
|
equity: 'bg-primary',
|
|
revenue: 'bg-muted-foreground',
|
|
expense: 'bg-destructive',
|
|
untaxed_reserves: 'bg-warning',
|
|
}
|
|
|
|
const TYPE_LABELS: Record<AccountType, string> = {
|
|
asset: 'Tillgång',
|
|
liability: 'Skuld',
|
|
equity: 'Eget kapital',
|
|
revenue: 'Intäkt',
|
|
expense: 'Kostnad',
|
|
untaxed_reserves: 'Obeskattade reserver',
|
|
}
|
|
|
|
interface AccountNumberProps {
|
|
number: string
|
|
name?: string
|
|
showName?: boolean
|
|
size?: 'sm' | 'default'
|
|
className?: string
|
|
}
|
|
|
|
export function AccountNumber({
|
|
number,
|
|
name,
|
|
showName,
|
|
size = 'default',
|
|
className,
|
|
}: AccountNumberProps) {
|
|
// Loads the BAS chart chunk after mount and re-renders once names and
|
|
// descriptions for non-hardcoded accounts are available.
|
|
useBasReference()
|
|
// Inactive accounts included: historical verifikat keep referencing them
|
|
// long after they leave the active chart.
|
|
const { accounts } = useAccounts(false)
|
|
const companyName = useMemo(
|
|
() => accounts.find((a) => a.account_number === number)?.account_name,
|
|
[accounts, number],
|
|
)
|
|
const desc = getAccountDescription(number)
|
|
// The company's own account name wins over the BAS reference name: a chart
|
|
// row is user-editable data and must never be visually overridden by our
|
|
// hardcoded copy. Call sites that already know the row pass `name`; the
|
|
// rest (verifikat views) fall back to the shared reference-data cache, so
|
|
// off-catalog accounts (e.g. SIE-imported 1580) still get their names.
|
|
// The tooltip still shows the BAS name as reference.
|
|
const displayName = name || companyName || desc?.name
|
|
|
|
const numberElement = (
|
|
<span
|
|
className={cn(
|
|
'font-mono',
|
|
size === 'sm' ? 'text-xs' : 'text-sm',
|
|
className,
|
|
)}
|
|
>
|
|
{number}
|
|
</span>
|
|
)
|
|
|
|
if (!desc) {
|
|
return (
|
|
<>
|
|
{numberElement}
|
|
{showName && displayName && (
|
|
<span className="ml-1">{displayName}</span>
|
|
)}
|
|
</>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<Tooltip>
|
|
<TooltipTrigger asChild>
|
|
<span
|
|
className={cn(
|
|
'inline-flex items-center gap-1 cursor-help',
|
|
'decoration-dotted underline underline-offset-4 decoration-muted-foreground/40',
|
|
)}
|
|
>
|
|
<span
|
|
className={cn(
|
|
'font-mono',
|
|
size === 'sm' ? 'text-xs' : 'text-sm',
|
|
className,
|
|
)}
|
|
>
|
|
{number}
|
|
</span>
|
|
{showName && displayName && (
|
|
<span className={size === 'sm' ? 'text-xs' : 'text-sm'}>
|
|
{displayName}
|
|
</span>
|
|
)}
|
|
</span>
|
|
</TooltipTrigger>
|
|
<TooltipContent side="top" className="max-w-xs p-3">
|
|
<div className="space-y-1.5">
|
|
<div className="flex items-center gap-2">
|
|
<span className={cn('h-2 w-2 rounded-full shrink-0', TYPE_COLORS[desc.type])} />
|
|
<span className="text-xs text-muted-foreground">
|
|
{desc.classLabel} · {TYPE_LABELS[desc.type]}
|
|
</span>
|
|
</div>
|
|
<div className="font-medium">
|
|
<span className="font-mono mr-1.5">{number}</span>
|
|
{desc.name}
|
|
</div>
|
|
<p className="text-xs text-muted-foreground leading-relaxed">
|
|
{desc.explanation}
|
|
</p>
|
|
</div>
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
)
|
|
}
|