Kostenstellenhierarchie und Auswertung mit Unterkostenstellen ergänzt

This commit is contained in:
2026-04-24 22:56:33 +02:00
parent 5869f88c1a
commit d3ab03da7e
8 changed files with 204 additions and 7 deletions

View File

@@ -0,0 +1,32 @@
<script setup>
const props = defineProps({
row: {
type: Object,
required: true,
default: {}
},
inShow: {
type: Boolean,
default: false
}
})
const value = computed(() => {
const costcentre = props.row.parentCostcentre || props.row.costcentre || props.row.costCentre || null
if (!costcentre) {
return ""
}
return [costcentre.number, costcentre.name].filter(Boolean).join(" - ")
})
</script>
<template>
<div v-if="props.row.parentCostcentre">
<nuxt-link v-if="props.inShow" :to="`/standardEntity/costcentres/show/${props.row.parentCostcentre.id}`">
{{ value }}
</nuxt-link>
<span v-else>{{ value }}</span>
</div>
</template>

View File

@@ -10,11 +10,57 @@ const props = defineProps({
const loading = ref(true)
const incomingInvoices = ref([])
const costcentres = ref([])
const selectedYear = ref(String(dayjs().year()))
const selectedMonth = ref("all")
const currency = (value) => `${Number(value || 0).toFixed(2).replace(".", ",")} EUR`
const costCentreMap = computed(() => {
return new Map(costcentres.value.map((costcentre) => [costcentre.id, costcentre]))
})
const relevantCostCentreIds = computed(() => {
const rootId = props.item?.id
if (!rootId) {
return new Set()
}
const childrenByParent = new Map()
costcentres.value.forEach((costcentre) => {
if (!costcentre.parentCostcentre) {
return
}
if (!childrenByParent.has(costcentre.parentCostcentre)) {
childrenByParent.set(costcentre.parentCostcentre, [])
}
childrenByParent.get(costcentre.parentCostcentre).push(costcentre.id)
})
const collectedIds = new Set([rootId])
const queue = [rootId]
while (queue.length > 0) {
const currentId = queue.shift()
const childIds = childrenByParent.get(currentId) || []
childIds.forEach((childId) => {
if (collectedIds.has(childId)) {
return
}
collectedIds.add(childId)
queue.push(childId)
})
}
return collectedIds
})
const yearItems = computed(() => {
const years = [...new Set(
incomingInvoices.value
@@ -29,7 +75,7 @@ const monthItems = [
{ label: "Ganzes Jahr", value: "all" },
{ label: "Januar", value: "1" },
{ label: "Februar", value: "2" },
{ label: "Maerz", value: "3" },
{ label: "März", value: "3" },
{ label: "April", value: "4" },
{ label: "Mai", value: "5" },
{ label: "Juni", value: "6" },
@@ -53,12 +99,15 @@ const reportRows = computed(() => {
return []
}
const matchingAccounts = (invoice.accounts || []).filter((account) => account.costCentre === props.item.id)
const matchingAccounts = (invoice.accounts || []).filter((account) =>
relevantCostCentreIds.value.has(account.costCentre)
)
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)
const accountCostCentre = costCentreMap.value.get(account.costCentre)
return {
id: `${invoice.id}-${index}`,
@@ -68,6 +117,7 @@ const reportRows = computed(() => {
state: invoice.state || "-",
vendorName: invoice.vendor?.name || "-",
accountLabel: account.account?.label || account.accountLabel || "-",
costCentreName: accountCostCentre ? `${accountCostCentre.number} - ${accountCostCentre.name}` : "-",
description: account.description || invoice.description || "-",
amountNet,
amountTax,
@@ -91,6 +141,7 @@ const columns = [
{ accessorKey: "date", header: "Datum" },
{ accessorKey: "vendorName", header: "Lieferant" },
{ accessorKey: "accountLabel", header: "Konto" },
{ accessorKey: "costCentreName", header: "Kostenstelle" },
{ accessorKey: "description", header: "Beschreibung" },
{ accessorKey: "amountNet", header: "Netto" },
{ accessorKey: "amountTax", header: "Steuer" },
@@ -100,10 +151,11 @@ const columns = [
const setupPage = async () => {
loading.value = true
costcentres.value = await useEntities("costcentres").select("*", null, false, true)
const invoices = await useEntities("incominginvoices").select("*, vendor(id,name)")
incomingInvoices.value = invoices.filter((invoice) =>
(invoice.accounts || []).some((account) => account.costCentre === props.item.id)
(invoice.accounts || []).some((account) => relevantCostCentreIds.value.has(account.costCentre))
)
const firstYear = yearItems.value[0]?.value
@@ -162,7 +214,7 @@ setupPage()
v-if="!loading"
:data="reportRows"
:columns="columns"
:empty="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Eingangsbelege mit dieser Kostenstelle gefunden' }"
:empty="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Eingangsbelege mit dieser Kostenstelle oder ihren Unterkostenstellen gefunden' }"
class="w-full"
>
<template #reference-cell="{ row }">
@@ -181,6 +233,10 @@ setupPage()
<div class="truncate">{{ row.original.accountLabel }}</div>
</template>
<template #costCentreName-cell="{ row }">
<div class="truncate">{{ row.original.costCentreName }}</div>
</template>
<template #description-cell="{ row }">
<UTooltip :text="row.original.description">
<div class="max-w-[18rem] truncate">{{ row.original.description }}</div>