3. Zwischenstand
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<script setup>
|
||||
import dayjs from "dayjs";
|
||||
const router = useRouter()
|
||||
import dayjs from "dayjs"
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const props = defineProps({
|
||||
queryStringData: {
|
||||
@@ -21,83 +21,260 @@ const props = defineProps({
|
||||
}
|
||||
})
|
||||
|
||||
const statementallocations = ref([])
|
||||
const incominginvoices = ref([])
|
||||
const loading = ref(true)
|
||||
const incomingInvoices = ref([])
|
||||
const statementAllocations = ref([])
|
||||
const selectedYear = ref(String(dayjs().year()))
|
||||
const selectedMonth = ref("all")
|
||||
|
||||
const currency = (value) => `${Number(value || 0).toFixed(2).replace(".", ",")} EUR`
|
||||
const currentAccountId = computed(() => String(props.item?.id ?? ""))
|
||||
const sameAccount = (value) => String(value ?? "") === currentAccountId.value
|
||||
const getStatementId = (allocation) => allocation?.bankstatement?.id || allocation?.bs_id?.id || allocation?.bs_id || null
|
||||
const getStatementLike = (allocation) => allocation?.bankstatement || (typeof allocation?.bs_id === "object" ? allocation.bs_id : null)
|
||||
const getAllocationDate = (allocation) => {
|
||||
const statement = getStatementLike(allocation)
|
||||
|
||||
return statement?.date || statement?.valueDate || allocation?.bs_id?.date || allocation?.bs_id?.valueDate || null
|
||||
}
|
||||
const getAllocationPartner = (allocation) => {
|
||||
const statement = getStatementLike(allocation)
|
||||
|
||||
return statement?.debName || statement?.credName || statement?.partner || allocation?.bs_id?.debName || allocation?.bs_id?.credName || ""
|
||||
}
|
||||
const getAllocationDescription = (allocation) => {
|
||||
const statement = getStatementLike(allocation)
|
||||
|
||||
return allocation?.description || statement?.purpose || statement?.text || statement?.description || allocation?.bs_id?.purpose || allocation?.bs_id?.text || ""
|
||||
}
|
||||
const hasContent = (value) => value !== null && value !== undefined && String(value).trim() !== ""
|
||||
|
||||
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 allAllocations = computed(() => {
|
||||
const statementRows = statementAllocations.value.map((allocation) => ({
|
||||
...allocation,
|
||||
type: "statementallocation",
|
||||
bankstatement: allocation.bankstatement || getStatementLike(allocation),
|
||||
date: getAllocationDate(allocation),
|
||||
partner: getAllocationPartner(allocation),
|
||||
description: getAllocationDescription(allocation),
|
||||
amount: Number(allocation.amount || 0)
|
||||
}))
|
||||
|
||||
const incomingInvoiceRows = incomingInvoices.value.flatMap((invoice) => {
|
||||
return (invoice.accounts || [])
|
||||
.filter((account) => sameAccount(account.account?.id || account.account))
|
||||
.map((account, index) => ({
|
||||
id: `${invoice.id}-${index}`,
|
||||
incominginvoiceid: invoice.id,
|
||||
type: "incominginvoice",
|
||||
amount: Number(account.amountGross || account.amountNet || 0),
|
||||
date: invoice.date,
|
||||
partner: invoice.vendor?.name || "",
|
||||
description: account.description || invoice.description || "",
|
||||
color: invoice.expense ? "red" : "green",
|
||||
expense: invoice.expense,
|
||||
reference: invoice.reference || "-"
|
||||
}))
|
||||
})
|
||||
|
||||
return [...statementRows, ...incomingInvoiceRows]
|
||||
})
|
||||
|
||||
const yearItems = computed(() => {
|
||||
const years = [...new Set(
|
||||
allAllocations.value
|
||||
.map((allocation) => allocation.bankstatement?.date || allocation.date)
|
||||
.filter(Boolean)
|
||||
.map((date) => String(dayjs(date).year()))
|
||||
)].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 renderedAllocations = computed(() => {
|
||||
return allAllocations.value.filter((allocation) => {
|
||||
const allocationDateValue = allocation.bankstatement?.date || allocation.date
|
||||
const allocationDate = allocationDateValue ? dayjs(allocationDateValue) : null
|
||||
|
||||
if (allocationDate && allocationDate.year().toString() !== selectedYear.value) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (allocationDate && selectedMonth.value !== "all" && allocationDate.month() + 1 !== Number(selectedMonth.value)) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
})
|
||||
|
||||
const totals = computed(() => {
|
||||
return renderedAllocations.value.reduce((acc, allocation) => {
|
||||
const amount = Number(allocation.amount || 0)
|
||||
|
||||
if (allocation.incominginvoiceid) {
|
||||
if (allocation.expense) {
|
||||
acc.expenses += amount
|
||||
acc.balance -= amount
|
||||
} else {
|
||||
acc.income += amount
|
||||
acc.balance += amount
|
||||
}
|
||||
} else {
|
||||
if (amount < 0) {
|
||||
acc.expenses += Math.abs(amount)
|
||||
} else {
|
||||
acc.income += amount
|
||||
}
|
||||
acc.balance += amount
|
||||
}
|
||||
|
||||
return acc
|
||||
}, { income: 0, expenses: 0, balance: 0 })
|
||||
})
|
||||
|
||||
const columns = [
|
||||
{ accessorKey: "amount", header: "Betrag" },
|
||||
{ accessorKey: "date", header: "Datum" },
|
||||
{ accessorKey: "partner", header: "Partner" },
|
||||
{ accessorKey: "description", header: "Beschreibung" }
|
||||
]
|
||||
|
||||
const setup = async () => {
|
||||
loading.value = true
|
||||
|
||||
statementAllocations.value = (await useEntities("statementallocations").select("*, bankstatement(*), createddocument(*), incominginvoice(*)"))
|
||||
.filter((allocation) => sameAccount(allocation.account?.id || allocation.account) || sameAccount(allocation.ownaccount?.id || allocation.ownaccount))
|
||||
|
||||
incomingInvoices.value = (await useEntities("incominginvoices").select("*, vendor(*)"))
|
||||
.filter((invoice) => (invoice.accounts || []).some((account) => sameAccount(account.account?.id || account.account)))
|
||||
|
||||
const firstYear = yearItems.value[0]?.value
|
||||
if (firstYear && !yearItems.value.some((item) => item.value === selectedYear.value)) {
|
||||
selectedYear.value = firstYear
|
||||
}
|
||||
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
setup()
|
||||
|
||||
const selectAllocation = (allocation) => {
|
||||
if(allocation.type === "statementallocation") {
|
||||
router.push(`/banking/statements/edit/${allocation.bs_id.id}`)
|
||||
} else if(allocation.type === "incominginvoice") {
|
||||
router.push(`/incominginvoices/show/${allocation.incominginvoiceid}`)
|
||||
const unwrapAllocationRow = (allocationLike) => allocationLike?.original || allocationLike
|
||||
|
||||
const selectAllocation = (allocationLike) => {
|
||||
const allocation = unwrapAllocationRow(allocationLike)
|
||||
|
||||
if (!allocation) {
|
||||
return
|
||||
}
|
||||
|
||||
const statementId = getStatementId(allocation)
|
||||
|
||||
if (allocation.type === "statementallocation" && statementId) {
|
||||
router.push(`/banking/statements/edit/${statementId}`)
|
||||
} else if (allocation.type === "incominginvoice" && allocation.incominginvoiceid) {
|
||||
router.push(`/incomingInvoices/show/${allocation.incominginvoiceid}`)
|
||||
}
|
||||
}
|
||||
|
||||
const renderedAllocations = computed(() => {
|
||||
|
||||
let tempstatementallocations = props.item.statementallocations.map(i => {
|
||||
return {
|
||||
...i,
|
||||
type: "statementallocation",
|
||||
date: i.bs_id.date,
|
||||
partner: i.bs_id ? (i.bs_id.debName ? i.bs_id.debName : (i.bs_id.credName ? i.bs_id.credName : '')) : ''
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
/*let incominginvoicesallocations = []
|
||||
|
||||
incominginvoices.value.forEach(i => {
|
||||
|
||||
incominginvoicesallocations.push(...i.accounts.filter(x => x.account == route.params.id).map(x => {
|
||||
return {
|
||||
...x,
|
||||
incominginvoiceid: i.id,
|
||||
type: "incominginvoice",
|
||||
amount: x.amountGross ? x.amountGross : x.amountNet,
|
||||
date: i.date,
|
||||
partner: i.vendor.name,
|
||||
description: i.description,
|
||||
color: i.expense ? "red" : "green"
|
||||
}
|
||||
}))
|
||||
})*/
|
||||
|
||||
return [...tempstatementallocations/*, ... incominginvoicesallocations*/]
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCard class="mt-5">
|
||||
<UTable
|
||||
v-if="props.item.statementallocations"
|
||||
:data="renderedAllocations"
|
||||
:columns="normalizeTableColumns([{key:'amount', label:'Betrag'},{key:'date', label:'Datum'},{key:'partner', label:'Partner'},{key:'description', label:'Beschreibung'}])"
|
||||
:on-select="(i) => selectAllocation(i)"
|
||||
:empty="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Buchungen anzuzeigen' }"
|
||||
>
|
||||
<template #amount-cell="{row}">
|
||||
<span class="text-right text-rose-600" v-if="row.original.amount < 0 || row.original.color === 'red'">{{useCurrency(row.original.amount)}}</span>
|
||||
<span class="text-right text-primary-500" v-else-if="row.original.amount > 0 || row.original.color === 'green'">{{useCurrency(row.original.amount)}}</span>
|
||||
<span v-else>{{useCurrency(row.original.amount)}}</span>
|
||||
</template>
|
||||
<template #date-cell="{row}">
|
||||
{{row.original.date ? dayjs(row.original.date).format('DD.MM.YYYY') : ''}}
|
||||
</template>
|
||||
<template #description-cell="{row}">
|
||||
{{row.original.description ? row.original.description : ''}}
|
||||
</template>
|
||||
</UTable>
|
||||
<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">Einnahmen</div>
|
||||
<div class="mt-1 text-xl font-semibold">{{ currency(totals.income) }}</div>
|
||||
</UCard>
|
||||
|
||||
<UCard>
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">Ausgaben</div>
|
||||
<div class="mt-1 text-xl font-semibold">{{ currency(totals.expenses) }}</div>
|
||||
</UCard>
|
||||
|
||||
<UCard>
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">Saldo</div>
|
||||
<div class="mt-1 text-xl font-semibold">{{ currency(totals.balance) }}</div>
|
||||
</UCard>
|
||||
</div>
|
||||
|
||||
<div v-if="!loading" class="overflow-auto max-h-[60vh]">
|
||||
<UTable
|
||||
:data="renderedAllocations"
|
||||
:columns="normalizeTableColumns(columns)"
|
||||
:on-select="selectAllocation"
|
||||
class="w-full"
|
||||
>
|
||||
<template #empty>
|
||||
<div class="flex flex-col items-center justify-center py-10 text-center">
|
||||
<UIcon name="i-heroicons-circle-stack-20-solid" class="mb-2 h-10 w-10 text-gray-400" />
|
||||
<p class="font-medium">Keine Buchungen im ausgewaehlten Zeitraum</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #amount-cell="{ row }">
|
||||
<span class="text-right text-error" v-if="row.original.amount < 0 || row.original.color === 'red'">{{ useCurrency(row.original.amount) }}</span>
|
||||
<span class="text-right text-primary-500" v-else-if="row.original.amount > 0 || row.original.color === 'green'">{{ useCurrency(row.original.amount) }}</span>
|
||||
<span v-else>{{ useCurrency(row.original.amount) }}</span>
|
||||
</template>
|
||||
|
||||
<template #date-cell="{ row }">
|
||||
{{ row.original.bankstatement.date && dayjs(row.original.bankstatement.date).isValid() ? dayjs(row.original.bankstatement.date).format('DD.MM.YYYY') : '-' }}
|
||||
</template>
|
||||
|
||||
<template #partner-cell="{ row }">
|
||||
<div class="truncate">{{ hasContent(row.original.partner) ? row.original.partner : '-' }}</div>
|
||||
</template>
|
||||
|
||||
<template #description-cell="{ row }">
|
||||
<UTooltip :text="hasContent(row.original.description) ? row.original.description : '-'">
|
||||
<div class="max-w-[22rem] truncate">{{ hasContent(row.original.description) ? row.original.description : '-' }}</div>
|
||||
</UTooltip>
|
||||
</template>
|
||||
</UTable>
|
||||
</div>
|
||||
|
||||
<UProgress v-else animation="carousel" class="w-3/4 mx-auto" />
|
||||
</div>
|
||||
</UCard>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user