3. Zwischenstand
This commit is contained in:
@@ -1,26 +1,205 @@
|
||||
<script setup>
|
||||
import dayjs from "dayjs"
|
||||
|
||||
const props = defineProps({
|
||||
item: {
|
||||
required: true,
|
||||
type: String
|
||||
type: Object
|
||||
}
|
||||
})
|
||||
|
||||
const incomingInvoices = ref({})
|
||||
const loading = ref(true)
|
||||
const incomingInvoices = ref([])
|
||||
const selectedYear = ref(String(dayjs().year()))
|
||||
const selectedMonth = ref("all")
|
||||
|
||||
const currency = (value) => `${Number(value || 0).toFixed(2).replace(".", ",")} EUR`
|
||||
|
||||
const yearItems = computed(() => {
|
||||
const years = [...new Set(
|
||||
incomingInvoices.value
|
||||
.map((invoice) => invoice.date ? String(dayjs(invoice.date).year()) : null)
|
||||
.filter(Boolean)
|
||||
)].sort((a, b) => Number(b) - Number(a))
|
||||
|
||||
return years.length > 0 ? years.map((year) => ({ label: year, value: year })) : [{ label: String(dayjs().year()), value: String(dayjs().year()) }]
|
||||
})
|
||||
|
||||
const monthItems = [
|
||||
{ label: "Ganzes Jahr", value: "all" },
|
||||
{ label: "Januar", value: "1" },
|
||||
{ label: "Februar", value: "2" },
|
||||
{ label: "Maerz", value: "3" },
|
||||
{ label: "April", value: "4" },
|
||||
{ label: "Mai", value: "5" },
|
||||
{ label: "Juni", value: "6" },
|
||||
{ label: "Juli", value: "7" },
|
||||
{ label: "August", value: "8" },
|
||||
{ label: "September", value: "9" },
|
||||
{ label: "Oktober", value: "10" },
|
||||
{ label: "November", value: "11" },
|
||||
{ label: "Dezember", value: "12" }
|
||||
]
|
||||
|
||||
const reportRows = computed(() => {
|
||||
return incomingInvoices.value.flatMap((invoice) => {
|
||||
const invoiceDate = invoice.date ? dayjs(invoice.date) : null
|
||||
|
||||
if (invoiceDate && invoiceDate.year().toString() !== selectedYear.value) {
|
||||
return []
|
||||
}
|
||||
|
||||
if (invoiceDate && selectedMonth.value !== "all" && invoiceDate.month() + 1 !== Number(selectedMonth.value)) {
|
||||
return []
|
||||
}
|
||||
|
||||
const matchingAccounts = (invoice.accounts || []).filter((account) => account.costCentre === props.item.id)
|
||||
|
||||
return matchingAccounts.map((account, index) => {
|
||||
const amountNet = Number(account.amountNet || 0)
|
||||
const amountTax = Number(account.amountTax || 0)
|
||||
const amountGross = Number(account.amountGross || amountNet + amountTax || 0)
|
||||
|
||||
return {
|
||||
id: `${invoice.id}-${index}`,
|
||||
invoiceId: invoice.id,
|
||||
reference: invoice.reference || "-",
|
||||
date: invoice.date,
|
||||
state: invoice.state || "-",
|
||||
vendorName: invoice.vendor?.name || "-",
|
||||
accountLabel: account.account?.label || account.accountLabel || "-",
|
||||
description: account.description || invoice.description || "-",
|
||||
amountNet,
|
||||
amountTax,
|
||||
amountGross
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
const totals = computed(() => {
|
||||
return reportRows.value.reduce((acc, row) => {
|
||||
acc.net += row.amountNet
|
||||
acc.tax += row.amountTax
|
||||
acc.gross += row.amountGross
|
||||
return acc
|
||||
}, { net: 0, tax: 0, gross: 0 })
|
||||
})
|
||||
|
||||
const columns = [
|
||||
{ accessorKey: "reference", header: "Beleg" },
|
||||
{ accessorKey: "date", header: "Datum" },
|
||||
{ accessorKey: "vendorName", header: "Lieferant" },
|
||||
{ accessorKey: "accountLabel", header: "Konto" },
|
||||
{ accessorKey: "description", header: "Beschreibung" },
|
||||
{ accessorKey: "amountNet", header: "Netto" },
|
||||
{ accessorKey: "amountTax", header: "Steuer" },
|
||||
{ accessorKey: "amountGross", header: "Brutto" }
|
||||
]
|
||||
|
||||
const setupPage = async () => {
|
||||
incomingInvoices.value = (await useEntities("incominginvoices").select()).filter(i => i.accounts.find(x => x.costCentre === props.item.id))
|
||||
loading.value = true
|
||||
|
||||
const invoices = await useEntities("incominginvoices").select("*, vendor(id,name)")
|
||||
|
||||
incomingInvoices.value = invoices.filter((invoice) =>
|
||||
(invoice.accounts || []).some((account) => account.costCentre === props.item.id)
|
||||
)
|
||||
|
||||
const firstYear = yearItems.value[0]?.value
|
||||
if (firstYear && !yearItems.value.some((item) => item.value === selectedYear.value)) {
|
||||
selectedYear.value = firstYear
|
||||
}
|
||||
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
setupPage()
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
{{props.item}}
|
||||
{{incomingInvoices}}
|
||||
<div class="space-y-4">
|
||||
<div class="flex flex-col gap-3 md:flex-row md:items-end">
|
||||
<UFormField label="Jahr" class="w-full md:w-48">
|
||||
<USelectMenu
|
||||
v-model="selectedYear"
|
||||
:items="yearItems"
|
||||
value-key="value"
|
||||
label-key="label"
|
||||
class="w-full"
|
||||
/>
|
||||
</UFormField>
|
||||
|
||||
<UFormField label="Monat" class="w-full md:w-56">
|
||||
<USelectMenu
|
||||
v-model="selectedMonth"
|
||||
:items="monthItems"
|
||||
value-key="value"
|
||||
label-key="label"
|
||||
class="w-full"
|
||||
/>
|
||||
</UFormField>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-3 md:grid-cols-3">
|
||||
<UCard>
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">Netto gesamt</div>
|
||||
<div class="mt-1 text-xl font-semibold">{{ currency(totals.net) }}</div>
|
||||
</UCard>
|
||||
|
||||
<UCard>
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">Steuer gesamt</div>
|
||||
<div class="mt-1 text-xl font-semibold">{{ currency(totals.tax) }}</div>
|
||||
</UCard>
|
||||
|
||||
<UCard>
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">Brutto gesamt</div>
|
||||
<div class="mt-1 text-xl font-semibold">{{ currency(totals.gross) }}</div>
|
||||
</UCard>
|
||||
</div>
|
||||
|
||||
<UTable
|
||||
v-if="!loading"
|
||||
:data="reportRows"
|
||||
:columns="columns"
|
||||
:empty="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Eingangsbelege mit dieser Kostenstelle gefunden' }"
|
||||
class="w-full"
|
||||
>
|
||||
<template #reference-cell="{ row }">
|
||||
<div class="truncate font-medium">{{ row.original.reference }}</div>
|
||||
</template>
|
||||
|
||||
<template #date-cell="{ row }">
|
||||
<div class="truncate">{{ row.original.date ? dayjs(row.original.date).format("DD.MM.YYYY") : "-" }}</div>
|
||||
</template>
|
||||
|
||||
<template #vendorName-cell="{ row }">
|
||||
<div class="truncate">{{ row.original.vendorName }}</div>
|
||||
</template>
|
||||
|
||||
<template #accountLabel-cell="{ row }">
|
||||
<div class="truncate">{{ row.original.accountLabel }}</div>
|
||||
</template>
|
||||
|
||||
<template #description-cell="{ row }">
|
||||
<UTooltip :text="row.original.description">
|
||||
<div class="max-w-[18rem] truncate">{{ row.original.description }}</div>
|
||||
</UTooltip>
|
||||
</template>
|
||||
|
||||
<template #amountNet-cell="{ row }">
|
||||
<div class="text-right tabular-nums">{{ currency(row.original.amountNet) }}</div>
|
||||
</template>
|
||||
|
||||
<template #amountTax-cell="{ row }">
|
||||
<div class="text-right tabular-nums">{{ currency(row.original.amountTax) }}</div>
|
||||
</template>
|
||||
|
||||
<template #amountGross-cell="{ row }">
|
||||
<div class="text-right font-medium tabular-nums">{{ currency(row.original.amountGross) }}</div>
|
||||
</template>
|
||||
</UTable>
|
||||
|
||||
<UProgress v-else animation="carousel" class="w-3/4 mx-auto" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user