'use client' import { useState } from 'react' import { Plus, Receipt, FileCheck2, AlertTriangle, Download, FileText, Euro, Clock, } from 'lucide-react' import { useFetch } from '@/hooks/use-fetch' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Skeleton } from '@/components/ui/skeleton' import { PageHeader } from '../primitives' import { InvoiceDetailDialog, type Invoice } from '../invoice-dialog' import { formatEuro, formatDate, daysUntil } from '@/lib/format' import { cn } from '@/lib/utils' interface FinanceData { invoices: Invoice[] stats: { total: number draft: number sent: number paid: number overdue: number zugferdCount: number xrechnungCount: number datevCount: number outstandingCents: number } } const STATUS_META: Record = { draft: { label: 'Entwurf', cls: 'bg-muted text-foreground' }, sent: { label: 'Versendet', cls: 'bg-foreground/70 text-background' }, paid: { label: 'Bezahlt', cls: 'bg-foreground text-background' }, overdue: { label: 'Überfällig', cls: 'bg-foreground text-background' }, cancelled: { label: 'Storniert', cls: 'bg-muted text-muted-foreground' }, storno: { label: 'Storno', cls: 'bg-muted text-muted-foreground' }, } const TYPE_LABELS: Record = { final: 'Schlussrechnung', partial: 'Abschlag', recurring: 'Wiederkehrend', credit: 'Gutschrift', } export function FinanceView() { const { data, loading, refetch } = useFetch('/api/aos/finance') const [selected, setSelected] = useState(null) const [dialogOpen, setDialogOpen] = useState(false) if (loading || !data) { return (
) } const { invoices, stats } = data return (
Rechnung } />

Offene Forderungen

{formatEuro(stats.outstandingCents)}

Rechnungen gesamt

{stats.total}

{stats.draft} Entwurf · {stats.overdue} überfällig

ZUGFeRD / XRechnung

{stats.zugferdCount} / {stats.xrechnungCount}

E-Rechnung-konform

DATEV exportiert

{stats.datevCount}

{/* Compliance notice */}

E-Rechnung-Pflicht (ab 2025 Empfang, ab 2027/2028 Versand je nach Umsatz).

Validierung gem. EN 16931 über @e-invoice-eu/core. GoBD: Rechnungen 8 Jahre, Geschäftsbriefe 6 Jahre (mit Steuerberater bestätigen).

Rechnungen
{invoices.map((inv) => { const st = STATUS_META[inv.status] ?? STATUS_META.draft const dueDays = daysUntil(inv.dueDate) return ( { setSelected(inv); setDialogOpen(true) }} > ) })}
Nr. Kunde Typ Datum Fällig Netto Brutto E-Rechnung Status
{inv.number} {inv.company.name} {TYPE_LABELS[inv.type] ?? inv.type} {formatDate(inv.issueDate)} {formatDate(inv.dueDate)} {inv.status === 'sent' && dueDays < 0 && ( ({Math.abs(dueDays)} d überfällig) )} {formatEuro(inv.netCents)} {formatEuro(inv.grossCents)}
{inv.zugferd && ( ZUGFeRD )} {inv.xrechnung && ( XRechnung )} {inv.datevExported && ( DATEV )} {!inv.zugferd && !inv.xrechnung && !inv.datevExported && ( — )}
{st.label}
{/* Gapless numbering + finalize info */}

Lückenlose Nummerierung

RE-2026-0042 → RE-2026-0043 … GoBD-konform, kein Löschen, Korrekturen als Storno (neue Zeile). Finalisierungs-Sperre verhindert Nachträgliches Editieren.

Time-to-Invoice: 0 manuelle Schritte

Freigegebene Zeiteinträge werden direkt zu Rechnungspositionen. Quote → Projekt-Budget → Rechnungsplan → DATEV-Export.

) }