Rechnungsentwürfe optional wie Rechnungen verwenden

This commit is contained in:
2026-04-23 21:49:45 +02:00
parent 35ef3a7cf8
commit f4edcc2d44

View File

@@ -17,6 +17,16 @@ const dismissedRecurringKeys = computed(() => {
return tempStore.settings?.liquidityForecast?.dismissedRecurringKeys || [] return tempStore.settings?.liquidityForecast?.dismissedRecurringKeys || []
}) })
const includeDraftsAsInvoices = computed({
get: () => Boolean(tempStore.settings?.liquidityForecast?.includeDraftsAsInvoices),
set: (value) => {
tempStore.modifySettings("liquidityForecast", {
...(tempStore.settings?.liquidityForecast || {}),
includeDraftsAsInvoices: Boolean(value)
})
}
})
const storeDismissedRecurringKeys = (keys) => { const storeDismissedRecurringKeys = (keys) => {
tempStore.modifySettings("liquidityForecast", { tempStore.modifySettings("liquidityForecast", {
...(tempStore.settings?.liquidityForecast || {}), ...(tempStore.settings?.liquidityForecast || {}),
@@ -60,13 +70,16 @@ const readForecastCache = () => {
} }
} }
const applyDismissedRecurring = (value) => { const applyForecastAdjustments = (value) => {
if (!value) return null if (!value) return null
const dismissed = new Set(dismissedRecurringKeys.value) const dismissed = new Set(dismissedRecurringKeys.value)
const events = (value.events || []).filter((event) => { const baseEvents = (value.events || []).filter((event) => {
return event.source !== "recurring_bankstatement" || !event.recurringKey || !dismissed.has(event.recurringKey) return event.source !== "recurring_bankstatement" || !event.recurringKey || !dismissed.has(event.recurringKey)
}) })
const events = includeDraftsAsInvoices.value
? [...baseEvents, ...(value.draftEvents || [])]
: baseEvents
const recurring = (value.recurring || []).filter((item) => !item.key || !dismissed.has(item.key)) const recurring = (value.recurring || []).filter((item) => !item.key || !dismissed.has(item.key))
const eventsByDate = new Map() const eventsByDate = new Map()
@@ -109,7 +122,7 @@ const applyDismissedRecurring = (value) => {
const setRawForecast = (value) => { const setRawForecast = (value) => {
rawForecast.value = value rawForecast.value = value
forecast.value = applyDismissedRecurring(value) forecast.value = applyForecastAdjustments(value)
} }
const loadForecastFromCache = () => { const loadForecastFromCache = () => {
@@ -170,7 +183,7 @@ const dismissRecurringKey = async (key) => {
if (!key) return if (!key) return
storeDismissedRecurringKeys([...dismissedRecurringKeys.value, key]) storeDismissedRecurringKeys([...dismissedRecurringKeys.value, key])
forecast.value = applyDismissedRecurring(rawForecast.value) forecast.value = applyForecastAdjustments(rawForecast.value)
toast.add({ toast.add({
title: "Bankbewegung abgeschlossen", title: "Bankbewegung abgeschlossen",
description: "Das erkannte Muster wird aus der Liquiditätsprognose entfernt.", description: "Das erkannte Muster wird aus der Liquiditätsprognose entfernt.",
@@ -180,7 +193,7 @@ const dismissRecurringKey = async (key) => {
const restoreDismissedRecurring = () => { const restoreDismissedRecurring = () => {
storeDismissedRecurringKeys([]) storeDismissedRecurringKeys([])
forecast.value = applyDismissedRecurring(rawForecast.value) forecast.value = applyForecastAdjustments(rawForecast.value)
toast.add({ toast.add({
title: "Bankbewegungen wiederhergestellt", title: "Bankbewegungen wiederhergestellt",
description: "Ausgeblendete Muster werden wieder in der Prognose berücksichtigt.", description: "Ausgeblendete Muster werden wieder in der Prognose berücksichtigt.",
@@ -248,6 +261,7 @@ const groupedEventSummary = computed(() => {
open_createddocument: { label: sourceLabels.open_createddocument, amount: 0, count: 0 }, open_createddocument: { label: sourceLabels.open_createddocument, amount: 0, count: 0 },
open_incominginvoice: { label: sourceLabels.open_incominginvoice, amount: 0, count: 0 }, open_incominginvoice: { label: sourceLabels.open_incominginvoice, amount: 0, count: 0 },
recurring_bankstatement: { label: sourceLabels.recurring_bankstatement, amount: 0, count: 0 }, recurring_bankstatement: { label: sourceLabels.recurring_bankstatement, amount: 0, count: 0 },
draft_createddocument: { label: "Rechnungsentwürfe als Rechnungen", amount: 0, count: 0 },
tax_settlement: { label: sourceLabels.tax_settlement, amount: 0, count: 0 } tax_settlement: { label: sourceLabels.tax_settlement, amount: 0, count: 0 }
} }
@@ -275,6 +289,8 @@ const topExpenseDrivers = computed(() => {
}) })
const draftIncomeDrivers = computed(() => { const draftIncomeDrivers = computed(() => {
if (includeDraftsAsInvoices.value) return []
return [...(forecast.value?.draftEvents || [])] return [...(forecast.value?.draftEvents || [])]
.sort((a, b) => Number(b.amount || 0) - Number(a.amount || 0)) .sort((a, b) => Number(b.amount || 0) - Number(a.amount || 0))
.slice(0, 12) .slice(0, 12)
@@ -323,7 +339,11 @@ const riskTone = computed(() => {
}) })
watch(dismissedRecurringKeys, () => { watch(dismissedRecurringKeys, () => {
forecast.value = applyDismissedRecurring(rawForecast.value) forecast.value = applyForecastAdjustments(rawForecast.value)
})
watch(includeDraftsAsInvoices, () => {
forecast.value = applyForecastAdjustments(rawForecast.value)
}) })
onMounted(() => { onMounted(() => {
@@ -342,6 +362,11 @@ onMounted(() => {
</div> </div>
<div class="flex flex-wrap items-center gap-2"> <div class="flex flex-wrap items-center gap-2">
<div class="flex items-center gap-2 rounded-lg border border-gray-200 px-3 py-2 text-sm dark:border-gray-800">
<USwitch v-model="includeDraftsAsInvoices" />
<span class="text-gray-600 dark:text-gray-300">Entwürfe wie Rechnungen verwenden</span>
</div>
<UButton <UButton
v-if="dismissedRecurringKeys.length" v-if="dismissedRecurringKeys.length"
icon="i-heroicons-eye" icon="i-heroicons-eye"
@@ -452,7 +477,11 @@ onMounted(() => {
<span class="text-sm text-gray-500">Geplante Einzahlungen</span> <span class="text-sm text-gray-500">Geplante Einzahlungen</span>
<span class="font-semibold text-primary-600">+ {{ useCurrency(forecast.totalIncome) }}</span> <span class="font-semibold text-primary-600">+ {{ useCurrency(forecast.totalIncome) }}</span>
</div> </div>
<div v-if="forecast.draftIncome" class="flex items-center justify-between gap-3"> <div v-if="includeDraftsAsInvoices" class="flex items-center justify-between gap-3">
<span class="text-sm text-gray-500">Davon aus Entwürfen</span>
<span class="font-semibold text-primary-600">+ {{ useCurrency(rawForecast?.draftIncome || 0) }}</span>
</div>
<div v-else-if="forecast.draftIncome" class="flex items-center justify-between gap-3">
<span class="text-sm text-gray-500">Rechnungsentwürfe optional</span> <span class="text-sm text-gray-500">Rechnungsentwürfe optional</span>
<span class="font-semibold text-gray-500">(+ {{ useCurrency(forecast.draftIncome) }})</span> <span class="font-semibold text-gray-500">(+ {{ useCurrency(forecast.draftIncome) }})</span>
</div> </div>