4. Zwischenstand
All checks were successful
Build and Push Docker Images / build-backend (push) Successful in 15s
Build and Push Docker Images / build-frontend (push) Successful in 1m0s

This commit is contained in:
2026-03-22 17:43:41 +01:00
parent 9f665fc3b8
commit 11a242d70d
7 changed files with 724 additions and 420 deletions

View File

@@ -34,21 +34,21 @@ const monthItems = [
]
const accountColumns = [
{ accessorKey: "number", header: "Nummer" },
{ accessorKey: "label", header: "Konto" },
{ accessorKey: "bookings", header: "Buchungen" },
{ accessorKey: "gross", header: "Brutto" },
{ accessorKey: "net", header: "Netto" },
{ accessorKey: "tax", header: "Steuer" },
{ accessorKey: "gross", header: "Brutto" }
{ accessorKey: "number", header: "Nummer" },
{ accessorKey: "label", header: "Konto" },
{ accessorKey: "bookings", header: "Buchungen" }
]
const ownAccountColumns = [
{ accessorKey: "balance", header: "Saldo" },
{ accessorKey: "expenses", header: "Ausgaben" },
{ accessorKey: "income", header: "Einnahmen" },
{ accessorKey: "number", header: "Nummer" },
{ accessorKey: "label", header: "Konto" },
{ accessorKey: "bookings", header: "Buchungen" },
{ accessorKey: "income", header: "Einnahmen" },
{ accessorKey: "expenses", header: "Ausgaben" },
{ accessorKey: "balance", header: "Saldo" }
{ accessorKey: "bookings", header: "Buchungen" }
]
const isRelevantOutputDocument = (doc: any) => {
@@ -148,18 +148,36 @@ const filteredStatementAllocations = computed(() => {
return statementAllocations.value.filter((allocation) => matchesSelectedPeriod(getStatementDate(allocation)))
})
const filteredAccountStatementAllocations = computed(() => {
return filteredStatementAllocations.value.filter((allocation) => allocation.account !== null && allocation.account !== undefined)
})
const incomeTotal = computed(() => {
return Number(filteredDocuments.value.reduce((sum, doc) => sum + computeDocumentNet(doc), 0).toFixed(2))
})
const expenseNetTotal = computed(() => {
return Number(filteredIncomingInvoices.value.reduce((sum, invoice) => {
const invoiceExpenses = filteredIncomingInvoices.value.reduce((sum, invoice) => {
return sum + (invoice.accounts || []).reduce((accountSum: number, account: any) => accountSum + Number(account.amountNet || 0), 0)
}, 0).toFixed(2))
}, 0)
const directAccountExpenses = filteredAccountStatementAllocations.value.reduce((sum, allocation) => {
const amount = Number(allocation.amount || 0)
return amount < 0 ? sum + Math.abs(amount) : sum
}, 0)
return Number((invoiceExpenses + directAccountExpenses).toFixed(2))
})
const expenseGrossTotal = computed(() => {
return Number(filteredIncomingInvoices.value.reduce((sum, invoice) => sum + computeIncomingInvoiceGross(invoice), 0).toFixed(2))
const invoiceExpenses = filteredIncomingInvoices.value.reduce((sum, invoice) => sum + computeIncomingInvoiceGross(invoice), 0)
const directAccountExpenses = filteredAccountStatementAllocations.value.reduce((sum, allocation) => {
const amount = Number(allocation.amount || 0)
return amount < 0 ? sum + Math.abs(amount) : sum
}, 0)
return Number((invoiceExpenses + directAccountExpenses).toFixed(2))
})
const taxSummary = computed(() => {
@@ -201,14 +219,42 @@ const operatingResult = computed(() => {
return Number((incomeTotal.value - expenseNetTotal.value).toFixed(2))
})
const incomeDocumentCount = computed(() => filteredDocuments.value.length)
const expenseDocumentCount = computed(() => {
return filteredIncomingInvoices.value.length + filteredAccountStatementAllocations.value.length
})
const accountRows = computed(() => {
return accounts.value
.map((account) => {
const bookings = filteredIncomingInvoices.value.flatMap((invoice) => {
const invoiceBookings = filteredIncomingInvoices.value.flatMap((invoice) => {
return (invoice.accounts || [])
.filter((invoiceAccount: any) => sameId(invoiceAccount.account?.id || invoiceAccount.account, account.id))
.map((invoiceAccount: any) => ({
type: "incominginvoice",
amountNet: Number(invoiceAccount.amountNet || 0),
amountTax: Number(invoiceAccount.amountTax || 0),
amountGross: Number.isFinite(Number(invoiceAccount.amountGross))
? Number(invoiceAccount.amountGross)
: Number(invoiceAccount.amountNet || 0) + Number(invoiceAccount.amountTax || 0)
}))
})
const directBookings = filteredAccountStatementAllocations.value
.filter((allocation) => sameId(allocation.account?.id || allocation.account, account.id))
.map((allocation) => {
const amount = Number(allocation.amount || 0)
return {
type: "statementallocation",
amountNet: amount,
amountTax: 0,
amountGross: amount
}
})
const bookings = [...invoiceBookings, ...directBookings]
if (bookings.length === 0) {
return null
}
@@ -231,7 +277,7 @@ const accountRows = computed(() => {
}
})
.filter(Boolean)
.sort((left: any, right: any) => Number(right.gross) - Number(left.gross))
.sort((left: any, right: any) => Math.abs(Number(right.gross)) - Math.abs(Number(left.gross)))
})
const ownAccountRows = computed(() => {
@@ -315,16 +361,7 @@ onMounted(setupPage)
<template>
<UDashboardNavbar title="BWA">
<template #right>
<UButton
icon="i-heroicons-arrow-path"
variant="outline"
:loading="loading"
@click="setupPage"
>
Aktualisieren
</UButton>
</template>
</UDashboardNavbar>
<UDashboardPanelContent class="min-w-0 space-y-6 overflow-x-hidden overflow-y-auto p-4 md:p-6">
@@ -350,103 +387,121 @@ onMounted(setupPage)
</UFormField>
</div>
<div class="grid min-w-0 gap-4 md:grid-cols-2 xl:grid-cols-4">
<UCard class="min-w-0">
<div class="text-sm text-gray-500 dark:text-gray-400">Einnahmen netto</div>
<div class="mt-2 text-2xl font-semibold">{{ useCurrency(incomeTotal) }}</div>
<div class="mt-2 text-sm text-gray-500 dark:text-gray-400">
{{ filteredDocuments.length }} gebuchte Ausgangsbelege
</div>
</UCard>
<div class="grid min-w-0 gap-4 md:grid-cols-2 xl:grid-cols-4">
<UCard class="min-w-0">
<div class="text-sm text-gray-500 dark:text-gray-400">Einnahmen netto</div>
<div class="mt-2 text-2xl font-semibold">{{ useCurrency(incomeTotal) }}</div>
<div class="mt-2 text-sm text-gray-500 dark:text-gray-400">
{{ incomeDocumentCount }} gebuchte Ausgangsbelege
</div>
</UCard>
<UCard class="min-w-0">
<div class="text-sm text-gray-500 dark:text-gray-400">Ausgaben netto</div>
<UCard class="min-w-0">
<div class="text-sm text-gray-500 dark:text-gray-400">Ausgaben netto</div>
<div class="mt-2 text-2xl font-semibold">{{ useCurrency(expenseNetTotal) }}</div>
<div class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Brutto: {{ useCurrency(expenseGrossTotal) }}
</div>
</UCard>
<div class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Brutto: {{ useCurrency(expenseGrossTotal) }}
</div>
</UCard>
<UCard class="min-w-0">
<div class="text-sm text-gray-500 dark:text-gray-400">Betriebsergebnis</div>
<div class="mt-2 text-2xl font-semibold" :class="operatingResult >= 0 ? 'text-primary-500' : 'text-error'">
{{ useCurrency(operatingResult) }}
</div>
<div class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Einnahmen minus Ausgaben netto
</div>
</UCard>
<UCard class="min-w-0">
<div class="text-sm text-gray-500 dark:text-gray-400">Einnahmen Belege</div>
<div class="mt-2 text-2xl font-semibold">{{ incomeDocumentCount }}</div>
<div class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Ausgangsbelege im Zeitraum
</div>
</UCard>
<UCard class="min-w-0">
<div class="text-sm text-gray-500 dark:text-gray-400">USt-Saldo</div>
<div class="mt-2 text-2xl font-semibold" :class="taxSummary.balance >= 0 ? 'text-amber-600 dark:text-amber-400' : 'text-primary-500'">
{{ useCurrency(taxSummary.balance) }}
</div>
<div class="mt-2 text-sm text-gray-500 dark:text-gray-400">
USt {{ useCurrency(taxSummary.outputTax) }} | Vorsteuer {{ useCurrency(taxSummary.inputTax) }}
</div>
</UCard>
</div>
<UCard class="min-w-0">
<div class="text-sm text-gray-500 dark:text-gray-400">Ausgaben Belege</div>
<div class="mt-2 text-2xl font-semibold">{{ expenseDocumentCount }}</div>
<div class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Eingangsbelege plus direkte Buchungen
</div>
</UCard>
</div>
<div class="grid min-w-0 gap-4 xl:grid-cols-2">
<UCard class="min-w-0">
<template #header>
<div class="font-semibold">USt-Details</div>
</template>
<div class="grid min-w-0 gap-4 md:grid-cols-2">
<UCard class="min-w-0">
<div class="text-sm text-gray-500 dark:text-gray-400">Betriebsergebnis</div>
<div class="mt-2 text-2xl font-semibold" :class="operatingResult >= 0 ? 'text-primary-500' : 'text-error'">
{{ useCurrency(operatingResult) }}
</div>
<div class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Einnahmen minus Ausgaben netto
</div>
</UCard>
<div class="space-y-3 text-sm">
<div class="flex items-center justify-between">
<span>Netto 19% Ausgangsbelege</span>
<span>{{ useCurrency(taxSummary.output.net19) }}</span>
<UCard class="min-w-0">
<div class="text-sm text-gray-500 dark:text-gray-400">USt-Saldo</div>
<div class="mt-2 text-2xl font-semibold" :class="taxSummary.balance >= 0 ? 'text-amber-600 dark:text-amber-400' : 'text-primary-500'">
{{ useCurrency(taxSummary.balance) }}
</div>
<div class="flex items-center justify-between">
<span>USt 19%</span>
<span>{{ useCurrency(taxSummary.output.tax19) }}</span>
<div class="mt-2 text-sm text-gray-500 dark:text-gray-400">
USt {{ useCurrency(taxSummary.outputTax) }} | Vorsteuer {{ useCurrency(taxSummary.inputTax) }}
</div>
<div class="flex items-center justify-between">
<span>Netto 7% Ausgangsbelege</span>
<span>{{ useCurrency(taxSummary.output.net7) }}</span>
</div>
<div class="flex items-center justify-between">
<span>USt 7%</span>
<span>{{ useCurrency(taxSummary.output.tax7) }}</span>
</div>
<div class="flex items-center justify-between">
<span>Steuerfrei</span>
<span>{{ useCurrency(taxSummary.output.net0 + taxSummary.input.net0) }}</span>
</div>
</div>
</UCard>
</UCard>
</div>
<UCard class="min-w-0">
<template #header>
<div class="font-semibold">Vorsteuer-Details</div>
</template>
<div class="grid min-w-0 gap-4 xl:grid-cols-2">
<UCard class="min-w-0">
<template #header>
<div class="font-semibold">USt-Details</div>
</template>
<div class="space-y-3 text-sm">
<div class="flex items-center justify-between">
<span>Netto 19% Ausgangsbelege</span>
<span>{{ useCurrency(taxSummary.output.net19) }}</span>
</div>
<div class="flex items-center justify-between">
<span>USt 19%</span>
<span>{{ useCurrency(taxSummary.output.tax19) }}</span>
</div>
<div class="flex items-center justify-between">
<span>Netto 7% Ausgangsbelege</span>
<span>{{ useCurrency(taxSummary.output.net7) }}</span>
</div>
<div class="flex items-center justify-between">
<span>USt 7%</span>
<span>{{ useCurrency(taxSummary.output.tax7) }}</span>
</div>
<div class="flex items-center justify-between">
<span>Steuerfrei</span>
<span>{{ useCurrency(taxSummary.output.net0 + taxSummary.input.net0) }}</span>
</div>
</div>
</UCard>
<div class="space-y-3 text-sm">
<div class="flex items-center justify-between">
<span>Netto 19% Eingangsbelege</span>
<span>{{ useCurrency(taxSummary.input.net19) }}</span>
<UCard class="min-w-0">
<template #header>
<div class="font-semibold">Vorsteuer-Details</div>
</template>
<div class="space-y-3 text-sm">
<div class="flex items-center justify-between">
<span>Netto 19% Eingangsbelege</span>
<span>{{ useCurrency(taxSummary.input.net19) }}</span>
</div>
<div class="flex items-center justify-between">
<span>Vorsteuer 19%</span>
<span>{{ useCurrency(taxSummary.input.tax19) }}</span>
</div>
<div class="flex items-center justify-between">
<span>Netto 7% Eingangsbelege</span>
<span>{{ useCurrency(taxSummary.input.net7) }}</span>
</div>
<div class="flex items-center justify-between">
<span>Vorsteuer 7%</span>
<span>{{ useCurrency(taxSummary.input.tax7) }}</span>
</div>
<div class="flex items-center justify-between">
<span>Steuerfrei</span>
<span>{{ useCurrency(taxSummary.input.net0) }}</span>
</div>
</div>
<div class="flex items-center justify-between">
<span>Vorsteuer 19%</span>
<span>{{ useCurrency(taxSummary.input.tax19) }}</span>
</div>
<div class="flex items-center justify-between">
<span>Netto 7% Eingangsbelege</span>
<span>{{ useCurrency(taxSummary.input.net7) }}</span>
</div>
<div class="flex items-center justify-between">
<span>Vorsteuer 7%</span>
<span>{{ useCurrency(taxSummary.input.tax7) }}</span>
</div>
<div class="flex items-center justify-between">
<span>Steuerfrei</span>
<span>{{ useCurrency(taxSummary.input.net0) }}</span>
</div>
</div>
</UCard>
</div>
</UCard>
</div>
<div class="grid min-w-0 gap-4 xl:grid-cols-2">
<UCard class="min-w-0">