Added Abschreibungen
This commit is contained in:
@@ -3,6 +3,14 @@ import InputGroup from "~/components/InputGroup.vue";
|
||||
import dayjs from "dayjs";
|
||||
import { parseDate } from "@internationalized/date"
|
||||
import { useDraggable } from '@vueuse/core'
|
||||
import {
|
||||
DEPRECIATION_METHOD_ITEMS,
|
||||
EXPENSE_BOOKING_MODE_ITEMS,
|
||||
createIncomingInvoiceAccount,
|
||||
ensureDepreciationDefaults,
|
||||
isDepreciationBookingMode,
|
||||
normalizeIncomingInvoiceAccounts
|
||||
} from "~/composables/useDepreciation"
|
||||
|
||||
// --- Standard Setup & Data ---
|
||||
const dataStore = useDataStore()
|
||||
@@ -30,14 +38,7 @@ const itemInfo = ref({
|
||||
description: "",
|
||||
state: "Entwurf",
|
||||
accounts: [
|
||||
{
|
||||
account: null,
|
||||
amountNet: null,
|
||||
amountTax: null,
|
||||
taxType: "19",
|
||||
costCentre: null,
|
||||
amountGross: null
|
||||
}
|
||||
createIncomingInvoiceAccount()
|
||||
]
|
||||
})
|
||||
|
||||
@@ -61,12 +62,12 @@ const setup = async () => {
|
||||
itemInfo.value = {
|
||||
...invoiceData,
|
||||
vendor: invoiceData.vendor?.id || invoiceData.vendor,
|
||||
accounts: invoiceData.accounts || []
|
||||
accounts: normalizeIncomingInvoiceAccounts(invoiceData.accounts || [], invoiceData.date)
|
||||
}
|
||||
|
||||
// Fallback Accounts
|
||||
if(itemInfo.value.accounts.length === 0) {
|
||||
itemInfo.value.accounts.push({ account: null, amountNet: null, amountTax: null, taxType: "19", costCentre: null })
|
||||
itemInfo.value.accounts.push(createIncomingInvoiceAccount({ depreciationStartDate: itemInfo.value.date || null }))
|
||||
}
|
||||
|
||||
// Datei laden
|
||||
@@ -92,6 +93,14 @@ const setup = async () => {
|
||||
|
||||
setup()
|
||||
|
||||
watch(() => itemInfo.value.date, (value) => {
|
||||
;(itemInfo.value.accounts || []).forEach((account) => {
|
||||
if (isDepreciationItem(account) && !account.depreciationStartDate) {
|
||||
account.depreciationStartDate = value || null
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// --- Berechnungslogik ---
|
||||
const useNetMode = ref(false)
|
||||
|
||||
@@ -144,6 +153,11 @@ const totalCalculated = computed(() => {
|
||||
|
||||
const hasAmount = (value) => value !== null && value !== undefined && value !== ""
|
||||
const hasValidNumber = (value) => hasAmount(value) && Number.isFinite(Number(value))
|
||||
const isDepreciationItem = (item) => isDepreciationBookingMode(item?.bookingMode)
|
||||
|
||||
const updateBookingMode = (item) => {
|
||||
ensureDepreciationDefaults(item, itemInfo.value.date)
|
||||
}
|
||||
|
||||
const recalculateItem = (item, source) => {
|
||||
const taxRate = Number(taxOptions.value.find(i => i.key === item.taxType)?.percentage || 0);
|
||||
@@ -186,6 +200,7 @@ const updateIncomingInvoice = async (setBooked = false) => {
|
||||
}
|
||||
|
||||
let item = { ...itemInfo.value }
|
||||
item.accounts = (item.accounts || []).map((account) => ensureDepreciationDefaults({ ...account }, item.date))
|
||||
delete item.files
|
||||
item.state = setBooked ? "Gebucht" : "Entwurf"
|
||||
|
||||
@@ -205,6 +220,7 @@ const findIncomingInvoiceErrors = computed(() => {
|
||||
if(!i.accounts || i.accounts.length === 0) errors.push({message: "Es ist keine Position vorhanden", type: "breaking"})
|
||||
|
||||
i.accounts.forEach((account, idx) => {
|
||||
ensureDepreciationDefaults(account, i.date)
|
||||
if(!account.account) errors.push({message: `Pos ${idx+1}: Keine Kategorie`, type: "breaking"})
|
||||
if(!hasValidNumber(account.amountNet) && !hasValidNumber(account.amountGross)) {
|
||||
errors.push({message: `Pos ${idx+1}: Kein gültiger Betrag`, type: "breaking"})
|
||||
@@ -213,6 +229,12 @@ const findIncomingInvoiceErrors = computed(() => {
|
||||
if(hasValidNumber(account.amountNet) && !hasValidNumber(account.amountTax)) {
|
||||
errors.push({message: `Pos ${idx+1}: Steuerbetrag fehlt, bitte Steuer neu berechnen`, type: "warning"})
|
||||
}
|
||||
if(isDepreciationItem(account) && !Number(account.depreciationMonths)) {
|
||||
errors.push({message: `Pos ${idx+1}: Abschreibungsdauer fehlt`, type: "breaking"})
|
||||
}
|
||||
if(account.bookingMode === "depreciation_bundle" && !String(account.depreciationGroup || "").trim()) {
|
||||
errors.push({message: `Pos ${idx+1}: Sammelposten benötigt einen Gruppennamen`, type: "breaking"})
|
||||
}
|
||||
})
|
||||
|
||||
const order = { breaking: 0, warning: 1 }
|
||||
@@ -509,7 +531,7 @@ const hasBlockingIncomingInvoiceErrors = computed(() => blockingIncomingInvoiceE
|
||||
value-key="id"
|
||||
:disabled="mode === 'show'"
|
||||
:filter-fields="['label', 'number']"
|
||||
:color="item.account ? 'primary' : 'error'"
|
||||
:color="item.account ? 'primary' : 'error'"
|
||||
>
|
||||
<template #item="{ item: option }">
|
||||
<span class="font-mono text-xs text-gray-500 mr-2">{{ option.number }}</span> {{ option.label }}
|
||||
@@ -521,6 +543,20 @@ const hasBlockingIncomingInvoiceErrors = computed(() => blockingIncomingInvoiceE
|
||||
</UFormField>
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 md:col-span-6">
|
||||
<UFormField label="Aufwandsart">
|
||||
<USelectMenu
|
||||
class="w-full"
|
||||
:items="EXPENSE_BOOKING_MODE_ITEMS"
|
||||
value-key="value"
|
||||
label-key="label"
|
||||
v-model="item.bookingMode"
|
||||
:disabled="mode === 'show'"
|
||||
@update:model-value="updateBookingMode(item)"
|
||||
/>
|
||||
</UFormField>
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 md:col-span-6">
|
||||
<UFormField label="Kostenstelle">
|
||||
<USelectMenu
|
||||
@@ -539,6 +575,68 @@ const hasBlockingIncomingInvoiceErrors = computed(() => blockingIncomingInvoiceE
|
||||
</UFormField>
|
||||
</div>
|
||||
|
||||
<template v-if="isDepreciationItem(item)">
|
||||
<div class="col-span-12 md:col-span-4">
|
||||
<UFormField label="Abschreibungsdauer (Monate)">
|
||||
<UInput
|
||||
class="w-full"
|
||||
type="number"
|
||||
min="1"
|
||||
step="1"
|
||||
v-model="item.depreciationMonths"
|
||||
:disabled="mode === 'show'"
|
||||
/>
|
||||
</UFormField>
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 md:col-span-4">
|
||||
<UFormField label="Methode">
|
||||
<USelectMenu
|
||||
class="w-full"
|
||||
:items="DEPRECIATION_METHOD_ITEMS"
|
||||
value-key="value"
|
||||
label-key="label"
|
||||
v-model="item.depreciationMethod"
|
||||
:disabled="mode === 'show'"
|
||||
/>
|
||||
</UFormField>
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 md:col-span-4">
|
||||
<UFormField label="Start Abschreibung">
|
||||
<UInput
|
||||
class="w-full"
|
||||
type="date"
|
||||
v-model="item.depreciationStartDate"
|
||||
:disabled="mode === 'show'"
|
||||
/>
|
||||
</UFormField>
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 md:col-span-4">
|
||||
<UFormField :label="item.bookingMode === 'depreciation_bundle' ? 'Sammelposten' : 'Bezeichnung Abschreibung'">
|
||||
<UInput
|
||||
class="w-full"
|
||||
v-model="item[item.bookingMode === 'depreciation_bundle' ? 'depreciationGroup' : 'depreciationLabel']"
|
||||
:disabled="mode === 'show'"
|
||||
:placeholder="item.bookingMode === 'depreciation_bundle' ? 'z. B. IT-Hardware 2026' : 'z. B. Notebook Fuhrpark' "
|
||||
/>
|
||||
</UFormField>
|
||||
</div>
|
||||
|
||||
<div class="col-span-12">
|
||||
<UAlert
|
||||
color="primary"
|
||||
variant="soft"
|
||||
icon="i-heroicons-calendar-days"
|
||||
title="Nur die monatliche Abschreibung erscheint in der BWA"
|
||||
:description="item.bookingMode === 'depreciation_bundle'
|
||||
? 'Diese Position wird nicht sofort als Aufwand gezählt, sondern als Sammelposten periodisiert.'
|
||||
: 'Diese Position wird nicht sofort als Aufwand gezählt, sondern über die gewählte Laufzeit abgeschrieben.'"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="col-span-12 md:col-span-3">
|
||||
<UFormField label="Betrag (Netto)">
|
||||
<UInput
|
||||
@@ -624,7 +722,7 @@ const hasBlockingIncomingInvoiceErrors = computed(() => blockingIncomingInvoiceE
|
||||
icon="i-heroicons-plus"
|
||||
variant="soft"
|
||||
block
|
||||
@click="itemInfo.accounts.push({account:null, amountNet: null, amountTax:0, amountGross: null, taxType: '19'})"
|
||||
@click="itemInfo.accounts.push(createIncomingInvoiceAccount({ depreciationStartDate: itemInfo.date || null }))"
|
||||
>
|
||||
Weitere Position hinzufügen
|
||||
</UButton>
|
||||
|
||||
Reference in New Issue
Block a user