Files
accounted/components/ui/account-number.tsx
T
MattssonandClaude Fable 5 e41cf50afe fix(bookkeeping): verifikat views show company account names for off-catalog accounts (#1997)
* 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>
2026-08-27 17:45:52 +02:00

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} &middot; {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>
)
}