fix(bokslut): årsredovisning for a klarmarkerad year, bokslutsbilagor under Bokslut, bilagor reminder (#1875)
* fix(bokslut): årsredovisning for a klarmarkerad year, bokslutsbilagor under Bokslut in the menu, bilagor reminder on Kontroll A year closed in a previous system (Klarmarkera perioden) has no closing verifikat in these books by definition, so the statutory pre-closing guard in the trial balance has nothing to strip; it now lets that case through instead of failing the whole Årsredovisning page with a 500. Bokslutsbilagor now sits in the Bokslut fold of the menu (a byrå looks for the bilagor inside the bokslut module, not under Rapporter), and the wizard's Kontroll step says how many balance accounts are still unsigned per balansdagen with a link to the pärm. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RvFveUpbdPBXdm7f5FEYoz * fix(nav): admit the bokslutsbilagor label key Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RvFveUpbdPBXdm7f5FEYoz * feat(reconciliation): fold the manual accounts in the rail, with count and unsigned hint Twenty-odd balance accounts pushed the bank rows out of view on a migrated company. The group opens when a manual account is selected and otherwise remembers the last choice per browser. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RvFveUpbdPBXdm7f5FEYoz --------- 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
Claude Fable 5
Jakob Wennberg
parent
9ce1ebc65f
commit
3dce94d39e
@@ -45,6 +45,7 @@ import {
|
||||
PanelLeftClose,
|
||||
Library,
|
||||
BookCheck,
|
||||
FolderArchive,
|
||||
ShoppingCart,
|
||||
Car,
|
||||
} from 'lucide-react'
|
||||
@@ -134,6 +135,7 @@ type NavLabelKey =
|
||||
| 'deadlines'
|
||||
| 'periodiseringar'
|
||||
| 'year_end'
|
||||
| 'bokslutsbilagor'
|
||||
| 'annual_report'
|
||||
| 'income_declaration'
|
||||
| 'help'
|
||||
@@ -247,6 +249,7 @@ const navItems: NavItem[] = [
|
||||
{ href: '/deadlines', labelKey: 'deadlines', icon: CalendarClock, group: 'skatt' },
|
||||
{ href: '/bookkeeping/periodiseringar', labelKey: 'periodiseringar', icon: CalendarRange, group: 'skatt', fold: 'bokslut' },
|
||||
{ href: '/bookkeeping/year-end', labelKey: 'year_end', icon: FileCheck, group: 'skatt', fold: 'bokslut' },
|
||||
{ href: '/reports/bokslutsbilagor', labelKey: 'bokslutsbilagor', icon: FolderArchive, group: 'skatt', fold: 'bokslut' },
|
||||
{ href: '/bookkeeping/year-end/arsredovisning', labelKey: 'annual_report', icon: ScrollText, group: 'skatt', fold: 'bokslut', entityOnly: 'aktiebolag' },
|
||||
{ href: '/reports/ink2-declaration', labelKey: 'income_declaration', icon: FileSpreadsheet, group: 'skatt', fold: 'bokslut', entityOnly: 'aktiebolag' },
|
||||
{ href: '/reports/ne-declaration', labelKey: 'income_declaration', icon: FileSpreadsheet, group: 'skatt', fold: 'bokslut', entityOnly: 'enskild_firma' },
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
'use client'
|
||||
|
||||
import { Fragment } from 'react'
|
||||
import { Fragment, useState } from 'react'
|
||||
import Image from 'next/image'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import { ChevronDown, ChevronRight } from 'lucide-react'
|
||||
import { cn, formatDate } from '@/lib/utils'
|
||||
import type { ReconciliationAccount } from '@/lib/reconciliation/schemas'
|
||||
|
||||
@@ -63,10 +64,38 @@ interface ReconciliationRailProps {
|
||||
onSelect: (accountKey: string) => void
|
||||
}
|
||||
|
||||
const MANUAL_OPEN_STORAGE_KEY = 'Accounted:recon-rail-manual-open'
|
||||
|
||||
export function ReconciliationRail({ accounts, selectedKey, onSelect }: ReconciliationRailProps) {
|
||||
const t = useTranslations('reconciliation')
|
||||
const fed = accounts.filter((a) => a.kind !== 'manual')
|
||||
const manual = accounts.filter((a) => a.kind === 'manual')
|
||||
const selectedIsManual = selectedKey?.startsWith('manual:') ?? false
|
||||
|
||||
// The manual group folds: a migrated company has twenty-odd balance
|
||||
// accounts, and the bank rows must stay in view. It opens when a manual
|
||||
// account is selected and otherwise remembers the last choice per browser.
|
||||
const [manualOpen, setManualOpen] = useState<boolean>(() => {
|
||||
if (selectedIsManual) return true
|
||||
try {
|
||||
return window.localStorage.getItem(MANUAL_OPEN_STORAGE_KEY) === '1'
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
})
|
||||
// Derived, not synced: the selected row must stay visible.
|
||||
const showManual = manualOpen || selectedIsManual
|
||||
const toggleManual = () => {
|
||||
setManualOpen((open) => {
|
||||
try {
|
||||
window.localStorage.setItem(MANUAL_OPEN_STORAGE_KEY, open ? '0' : '1')
|
||||
} catch {
|
||||
// Per-browser convenience only.
|
||||
}
|
||||
return !open
|
||||
})
|
||||
}
|
||||
const manualUnsigned = manual.filter((a) => a.status?.state !== 'reconciled').length
|
||||
|
||||
const stateLabel = (account: ReconciliationAccount): string => {
|
||||
const state = account.status?.state ?? 'unknown'
|
||||
@@ -150,15 +179,31 @@ export function ReconciliationRail({ accounts, selectedKey, onSelect }: Reconcil
|
||||
{fed.map(renderRow)}
|
||||
{manual.length > 0 && (
|
||||
<Fragment>
|
||||
{fed.length > 0 && (
|
||||
<li
|
||||
aria-hidden
|
||||
className="mt-3 px-3 pb-1 text-[10.5px] font-semibold uppercase tracking-[0.08em] text-muted-foreground"
|
||||
<li className={cn(fed.length > 0 && 'mt-3')}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={toggleManual}
|
||||
aria-expanded={showManual}
|
||||
aria-controls="recon-rail-manual"
|
||||
className="flex w-full items-center gap-1.5 rounded-lg px-3 py-1.5 text-left text-[10.5px] font-semibold uppercase tracking-[0.08em] text-muted-foreground hover:bg-muted/60"
|
||||
>
|
||||
{t('rail_group_manual')}
|
||||
</li>
|
||||
{showManual ? (
|
||||
<ChevronDown className="h-3 w-3 shrink-0" aria-hidden="true" />
|
||||
) : (
|
||||
<ChevronRight className="h-3 w-3 shrink-0" aria-hidden="true" />
|
||||
)}
|
||||
<span className="min-w-0 flex-1 truncate">{t('rail_group_manual')}</span>
|
||||
<span className="shrink-0 normal-case tracking-normal tabular-nums" data-ph-mask>
|
||||
{manualUnsigned > 0 ? t('rail_group_manual_unsigned', { count: manualUnsigned, total: manual.length }) : String(manual.length)}
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
{showManual && (
|
||||
<Fragment>
|
||||
<li id="recon-rail-manual" className="sr-only" aria-hidden />
|
||||
{manual.map(renderRow)}
|
||||
</Fragment>
|
||||
)}
|
||||
{manual.map(renderRow)}
|
||||
</Fragment>
|
||||
)}
|
||||
</ul>
|
||||
|
||||
Reference in New Issue
Block a user