'use client' import { useEffect, useState } from 'react' import { Button } from '@/components/ui/button' import { Skeleton } from '@/components/ui/skeleton' import { Table, TableBody, TableHead, TableHeader, TableRow } from '@/components/ui/table' import { Download, AlertCircle, AlertTriangle, Info } from 'lucide-react' import { DeclarationRutaRow, formatWholeKronor, } from '@/components/reports/DeclarationRutaRow' import type { INK2Declaration } from '@/lib/reports/ink2/types' import { INK2R_RUTA_LABELS, INK2R_ASSET_CODES, INK2R_EQUITY_LIABILITY_CODES, INK2R_INCOME_CODES, } from '@/lib/reports/ink2/types' import { parseApiError } from './api-error' export function INK2DeclarationView({ periodId }: { periodId: string }) { // Fetch outcome tagged with the key it was requested under: switching // fiscal year discards stale responses instead of leaving last year's // declaration on screen (same pattern as the momsdeklaration view). const [result, setResult] = useState<{ key: string data?: INK2Declaration error?: string } | null>(null) const [retryKey, setRetryKey] = useState(0) const [downloading, setDownloading] = useState(false) const [downloadError, setDownloadError] = useState(null) const fetchKey = periodId ? `${periodId}:${retryKey}` : null useEffect(() => { if (!fetchKey || !periodId) return let cancelled = false fetch(`/api/reports/ink2?period_id=${periodId}`) .then(async (res) => { const json = await res.json().catch(() => null) if (cancelled) return if (!res.ok || json?.error) { setResult({ key: fetchKey, error: parseApiError(json?.error, 'Kunde inte hämta INK2-deklaration'), }) } else { setResult({ key: fetchKey, data: json.data }) } }) .catch(() => { if (!cancelled) { setResult({ key: fetchKey, error: 'Kunde inte hämta INK2-deklaration' }) } }) return () => { cancelled = true } }, [fetchKey, periodId]) const upToDate = result !== null && result.key === fetchKey const data = result?.data ?? null const error = upToDate ? (result.error ?? null) : null const loading = fetchKey !== null && !upToDate const downloadSRU = async () => { setDownloading(true) setDownloadError(null) try { const res = await fetch(`/api/reports/ink2?period_id=${periodId}&format=sru`) if (!res.ok) throw new Error('Download failed') const blob = await res.blob() const filename = res.headers.get('Content-Disposition')?.match(/filename="(.+)"/)?.[1] || 'INK2_SRU.zip' const url = URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = filename a.click() URL.revokeObjectURL(url) } catch { setDownloadError('Kunde inte ladda ner SRU-filer') } finally { setDownloading(false) } } if (error) { return (
{error}
) } if (loading && !data) { return (
) } if (!data) return null return (
{/* Header: company, year, filing artifact, and how to file it */}

INK2 (Aktiebolag)

{data.companyInfo.companyName} · {data.fiscalYear.name} {data.companyInfo.orgNumber && ` · Org.nr: ${data.companyInfo.orgNumber}`}

{downloadError && (
{downloadError}
)}
{/* Warnings */} {data.warnings.length > 0 && (
)} {/* Assets section */}

Tillgångar

Post Belopp {INK2R_ASSET_CODES.map((code) => ( ))}
Summa tillgångar {formatWholeKronor(data.totals.totalAssets)}
{/* Equity & Liabilities section */}

Eget kapital och skulder

Post Belopp {INK2R_EQUITY_LIABILITY_CODES.map((code) => ( ))}
Summa eget kapital och skulder {formatWholeKronor(data.totals.totalEquityLiabilities)}
{/* Income Statement section */}

Resultaträkning

Post Belopp {INK2R_INCOME_CODES.map((code) => ( ))}
Rörelseresultat = 0 ? 'text-success' : 'text-destructive' }`} > {formatWholeKronor(data.totals.operatingResult)}
Årets resultat = 0 ? 'text-success' : 'text-destructive' }`} > {formatWholeKronor(data.totals.aretsResultat)}
{/* INK2S summary */}

INK2S: Skattemässiga justeringar

Grundläggande justeringar och de återläggningar som sparats i bokslutet beräknas automatiskt. Kontrollera övriga deklarationsjusteringar med din redovisningskonsult.

Post Belopp {/* At most one of 4.1/4.2 is nonzero: render only that one. A zero-result year has both at 0, so both render then, or the table would be nothing but its total row. */}
{data.ink2s['8020'] > 0 ? 'Överskott (punkt 1.1)' : 'Underskott (punkt 1.2)'} 0 ? 'text-success' : 'text-destructive' }`} > {formatWholeKronor( data.ink2s['8020'] > 0 ? data.ink2s['8020'] : data.ink2s['8021'] )}
) }