import { useState, useEffect } from "react"; import { Link, useLoaderData } from "react-router"; import { requireUser } from "@/session.server"; import prisma from "@/lib/prisma.server"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { formatCurrency } from "@/lib/tax"; import { ChevronLeft, Scale, TrendingUp, Info } from "lucide-react"; import { KATEGORIE_LABELS } from "@/lib/ausgaben"; export const handle = { breadcrumbs: (data: { companyId: string; companyName: string }) => [ { label: "Mandanten", href: "/companies" }, { label: data.companyName, href: `/companies/${data.companyId}` }, { label: "Bilanzen" }, ], }; export async function loader({ request, params }: { request: Request; params: { id: string } }) { const user = await requireUser(request); const company = await prisma.company.findFirst({ where: { id: params.id, userId: user.id }, select: { id: true, name: true }, }); if (!company) throw new Response("Not Found", { status: 404 }); return { companyId: company.id, companyName: company.name }; } interface ErloeseByRate { netAmount: number; taxAmount: number; grossAmount: number; } interface BilanzenData { year: number; kleinunternehmer: boolean; guv: { erloeseByRate: Record; netTotal: number; taxTotal: number; grossTotal: number; invoiceCount: number; ausgabenGesamt: number; ausgabenByKategorie: { kategorie: string; betrag: number }[]; sonstigeEinnahmen: number; jahresergebnis: number; }; bilanz: { aktiva: { forderungen: { betrag: number; anzahl: number }; bank: { betrag: number; anzahl: number }; summe: number; }; passiva: { eigenkapital: number; summe: number; }; }; } function Row({ label, value, bold, indent, muted }: { label: string; value?: number; bold?: boolean; indent?: boolean; muted?: boolean; }) { return (
{label} {value !== undefined ? ( {formatCurrency(value)} ) : null}
); } export default function BilanzenPage() { const { companyId } = useLoaderData(); const [year, setYear] = useState(new Date().getFullYear()); const [data, setData] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { setLoading(true); fetch(`/api/bilanzen?companyId=${companyId}&year=${year}`) .then((r) => r.json()) .then((d) => { setData(d); setLoading(false); }); }, [companyId, year]); const years = Array.from({ length: 5 }, (_, i) => new Date().getFullYear() - i); return (
Zurück zum Mandanten

Bilanzen

Bilanz und Gewinn- & Verlustrechnung

{loading ? (
Lade Auswertung...
) : data && (
{/* Zusammenfassung */}

Umsatz (netto)

{formatCurrency(data.guv.netTotal)}

Betriebsausgaben

{formatCurrency(data.guv.ausgabenGesamt)}

Jahresergebnis

= 0 ? "text-teal-600" : "text-red-600"}`}> {formatCurrency(data.guv.jahresergebnis)}

Bilanzsumme

{formatCurrency(data.bilanz.aktiva.summe)}

{/* GuV */}
Gewinn- und Verlustrechnung (GuV) {year}

Erträge

{Object.entries(data.guv.erloeseByRate) .sort(([a], [b]) => Number(b) - Number(a)) .map(([rate, group]) => ( 0 ? `${rate}% MwSt.` : "steuerfrei"}` } value={group.netAmount} indent /> ))} {!data.kleinunternehmer && data.guv.taxTotal > 0 && ( )} {data.guv.sonstigeEinnahmen > 0 && (

Sonstige Einnahmen

)}

Aufwendungen

{data.guv.ausgabenByKategorie.length > 0 ? ( data.guv.ausgabenByKategorie .sort((a, b) => b.betrag - a.betrag) .map((a) => ( )) ) : ( )}
{data.guv.jahresergebnis >= 0 ? "Jahresüberschuss" : "Jahresfehlbetrag"} = 0 ? "text-teal-600" : "text-red-600"}`}> {formatCurrency(data.guv.jahresergebnis)}
{data.guv.ausgabenGesamt === 0 && (

Noch keine Betriebsausgaben für {data.year} erfasst. Ausgaben können über die Ausgaben-Seite gepflegt werden.

)}
{/* Bilanz */}
{/* Aktiva */}
Aktiva – Stichtag 31.12.{year}

Umlaufvermögen

Bank/Kasse ist eine Näherung auf Basis bezahlter Rechnungen (kumuliert bis Jahresende).

{/* Passiva */}
Passiva – Stichtag 31.12.{year}

Eigenkapital

Verbindlichkeiten

Verbindlichkeiten werden nicht erfasst. Das Eigenkapital entspricht vereinfacht der Aktivseite.

)}
); }