Compare commits
4 Commits
7d3e9a383f
...
e5fab70056
| Author | SHA1 | Date | |
|---|---|---|---|
| e5fab70056 | |||
| e7fe98da56 | |||
| e12ec4557d | |||
| 17717e8a1b |
@@ -6,9 +6,13 @@ let unallocatedStatements = ref(0)
|
|||||||
let bankaccounts = ref([])
|
let bankaccounts = ref([])
|
||||||
|
|
||||||
const setupPage = async () => {
|
const setupPage = async () => {
|
||||||
let bankstatements = (await useEntities("bankstatements").select("*, statementallocations(*)","date",true)).filter(i => !i.archived)
|
const [statementItems, accountItems] = await Promise.all([
|
||||||
|
useEntities("bankstatements").select("*, statementallocations(*)", "date", true),
|
||||||
|
useEntities("bankaccounts").select()
|
||||||
|
])
|
||||||
|
let bankstatements = statementItems.filter(i => !i.archived)
|
||||||
unallocatedStatements.value = bankstatements.filter(i => Number(calculateOpenSum(i)) !== 0).length
|
unallocatedStatements.value = bankstatements.filter(i => Number(calculateOpenSum(i)) !== 0).length
|
||||||
bankaccounts.value = await useEntities("bankaccounts").select()
|
bankaccounts.value = accountItems
|
||||||
}
|
}
|
||||||
|
|
||||||
setupPage()
|
setupPage()
|
||||||
@@ -51,4 +55,4 @@ const calculateOpenSum = (statement) => {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -10,9 +10,11 @@ let draftInvoicesSum = ref(0)
|
|||||||
let draftInvoicesCount = ref(0)
|
let draftInvoicesCount = ref(0)
|
||||||
|
|
||||||
let countPreparedOpenIncomingInvoices = ref(0)
|
let countPreparedOpenIncomingInvoices = ref(0)
|
||||||
|
const { loadCoreData } = useDashboardData()
|
||||||
|
|
||||||
const setupPage = async () => {
|
const setupPage = async () => {
|
||||||
let items = (await useEntities("createddocuments").select("*, statementallocations(*), customer(id,name), linkedDocument(*)")).filter(i => !i.archived)
|
const { createdDocuments, incomingInvoices } = await loadCoreData()
|
||||||
|
let items = createdDocuments.filter(i => !i.archived)
|
||||||
let documents = items.filter(i => i.type === "invoices" ||i.type === "advanceInvoices")
|
let documents = items.filter(i => i.type === "invoices" ||i.type === "advanceInvoices")
|
||||||
|
|
||||||
let draftDocuments = documents.filter(i => i.state === "Entwurf")
|
let draftDocuments = documents.filter(i => i.state === "Entwurf")
|
||||||
@@ -36,7 +38,7 @@ const setupPage = async () => {
|
|||||||
})
|
})
|
||||||
draftInvoicesCount.value = draftDocuments.length
|
draftInvoicesCount.value = draftDocuments.length
|
||||||
|
|
||||||
countPreparedOpenIncomingInvoices.value = (await useEntities("incominginvoices").select("id, state")).filter(i => i.state === "Vorbereitet" && !i.archived).length
|
countPreparedOpenIncomingInvoices.value = incomingInvoices.filter(i => i.state === "Vorbereitet" && !i.archived).length
|
||||||
}
|
}
|
||||||
|
|
||||||
setupPage()
|
setupPage()
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export const useDashboardData = () => {
|
|||||||
if (!force && cache.pendingRequest) return cache.pendingRequest
|
if (!force && cache.pendingRequest) return cache.pendingRequest
|
||||||
|
|
||||||
cache.pendingRequest = Promise.all([
|
cache.pendingRequest = Promise.all([
|
||||||
useEntities("createddocuments").select(),
|
useEntities("createddocuments").select("*, statementallocations(*), customer(id,name), linkedDocument(*)"),
|
||||||
useEntities("incominginvoices").select()
|
useEntities("incominginvoices").select()
|
||||||
])
|
])
|
||||||
.then(([createdDocuments, incomingInvoices]) => {
|
.then(([createdDocuments, incomingInvoices]) => {
|
||||||
|
|||||||
@@ -77,7 +77,10 @@ export const useFiles = () => {
|
|||||||
const selectDocument = async (id) => {
|
const selectDocument = async (id) => {
|
||||||
let documentIds = [id]
|
let documentIds = [id]
|
||||||
if(documentIds.length === 0) return []
|
if(documentIds.length === 0) return []
|
||||||
const fileData = await useEntities("files").selectSingle(id)
|
const fileData = await useEntities("files").selectSingle(
|
||||||
|
id,
|
||||||
|
"*, incominginvoice(*), project(*), vendor(*), customer(*), contract(*), plant(*), createddocument(*), vehicle(*), product(*), profile(*), check(*), inventoryitem(*)"
|
||||||
|
)
|
||||||
const res = await useNuxtApp().$api("/api/files/presigned",{
|
const res = await useNuxtApp().$api("/api/files/presigned",{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: {
|
body: {
|
||||||
|
|||||||
@@ -63,20 +63,24 @@ watch(searchString, (val) => {
|
|||||||
const setupPage = async () => {
|
const setupPage = async () => {
|
||||||
loadingDocs.value = true
|
loadingDocs.value = true
|
||||||
try {
|
try {
|
||||||
const [fRes, dRes, tRes] = await Promise.all([
|
const [fRes, dRes] = await Promise.all([
|
||||||
useEntities("folders").select(),
|
useEntities("folders").select(),
|
||||||
files.selectDocuments(),
|
useEntities("files").select("id, path, folder, type, createdAt")
|
||||||
useEntities("filetags").select()
|
|
||||||
])
|
])
|
||||||
folders.value = fRes || []
|
folders.value = fRes || []
|
||||||
documents.value = dRes || []
|
documents.value = dRes || []
|
||||||
filetags.value = tRes || []
|
|
||||||
|
|
||||||
syncCurrentFolderFromRoute()
|
syncCurrentFolderFromRoute()
|
||||||
} finally {
|
} finally {
|
||||||
loadingDocs.value = false
|
loadingDocs.value = false
|
||||||
loaded.value = true
|
loaded.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEntities("filetags").select()
|
||||||
|
.then((items) => {
|
||||||
|
filetags.value = items || []
|
||||||
|
})
|
||||||
|
.catch((error) => console.error("Dateitypen konnten nicht geladen werden:", error))
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Global Drag & Drop (Auto-Open Upload Modal) ---
|
// --- Global Drag & Drop (Auto-Open Upload Modal) ---
|
||||||
@@ -323,11 +327,22 @@ const updateName = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const showFile = (fileId) => {
|
const showFile = async (fileId) => {
|
||||||
modal.open(DocumentDisplayModal, {
|
loadingDocs.value = true
|
||||||
documentData: documents.value.find(i => i.id === fileId),
|
try {
|
||||||
onUpdatedNeeded: () => setupPage()
|
const documentData = await files.selectDocument(fileId)
|
||||||
})
|
if (!documentData) throw new Error("Datei nicht gefunden")
|
||||||
|
|
||||||
|
modal.open(DocumentDisplayModal, {
|
||||||
|
documentData,
|
||||||
|
onUpdatedNeeded: () => setupPage()
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
toast.add({title: 'Datei konnte nicht geöffnet werden', color: 'red'})
|
||||||
|
} finally {
|
||||||
|
loadingDocs.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const openScanModal = () => {
|
const openScanModal = () => {
|
||||||
|
|||||||
@@ -49,6 +49,11 @@ const loadedFileId = ref(null)
|
|||||||
const invoiceFiles = ref([])
|
const invoiceFiles = ref([])
|
||||||
const paymentTypeItems = ['Überweisung', 'Lastschrift', 'Kreditkarte', 'PayPal', 'Bar', 'Sonstiges']
|
const paymentTypeItems = ['Überweisung', 'Lastschrift', 'Kreditkarte', 'PayPal', 'Bar', 'Sonstiges']
|
||||||
const files = useFiles()
|
const files = useFiles()
|
||||||
|
const hasActiveBankAssignmentIn = (allocations = []) => allocations.some((allocation) => {
|
||||||
|
if (allocation?.archived) return false
|
||||||
|
|
||||||
|
return Boolean(allocation?.bankstatement || allocation?.bs_id)
|
||||||
|
})
|
||||||
|
|
||||||
const setup = async () => {
|
const setup = async () => {
|
||||||
// 1. Daten laden
|
// 1. Daten laden
|
||||||
@@ -65,6 +70,18 @@ const setup = async () => {
|
|||||||
accounts: normalizeIncomingInvoiceAccounts(invoiceData.accounts || [], invoiceData.date)
|
accounts: normalizeIncomingInvoiceAccounts(invoiceData.accounts || [], invoiceData.date)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mode.value === "edit" && invoiceData.state === "Gebucht" && (invoiceData.archived || hasActiveBankAssignmentIn(invoiceData.statementallocations))) {
|
||||||
|
toast.add({
|
||||||
|
title: "Bearbeiten nicht möglich",
|
||||||
|
description: invoiceData.archived
|
||||||
|
? "Archivierte Eingangsbelege können nicht bearbeitet werden."
|
||||||
|
: "Der Eingangsbeleg ist bereits einer Bankbuchung zugewiesen.",
|
||||||
|
color: "error"
|
||||||
|
})
|
||||||
|
await navigateTo(`/incomingInvoices/show/${invoiceData.id}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Fallback Accounts
|
// Fallback Accounts
|
||||||
if(itemInfo.value.accounts.length === 0) {
|
if(itemInfo.value.accounts.length === 0) {
|
||||||
itemInfo.value.accounts.push(createIncomingInvoiceAccount({ depreciationStartDate: itemInfo.value.date || null }))
|
itemInfo.value.accounts.push(createIncomingInvoiceAccount({ depreciationStartDate: itemInfo.value.date || null }))
|
||||||
@@ -150,6 +167,20 @@ const bankBookingDateLabel = computed(() => {
|
|||||||
|
|
||||||
return bankBookingDates.value.map(formatDate).join(", ")
|
return bankBookingDates.value.map(formatDate).join(", ")
|
||||||
})
|
})
|
||||||
|
const hasActiveBankAssignment = computed(() => hasActiveBankAssignmentIn(itemInfo.value.statementallocations))
|
||||||
|
const isBookedIncomingInvoice = computed(() => itemInfo.value.state === "Gebucht")
|
||||||
|
const canEditIncomingInvoice = computed(() => (
|
||||||
|
mode.value === "show"
|
||||||
|
&& isBookedIncomingInvoice.value
|
||||||
|
&& !itemInfo.value.archived
|
||||||
|
&& !hasActiveBankAssignment.value
|
||||||
|
))
|
||||||
|
const canArchiveIncomingInvoice = computed(() => {
|
||||||
|
if (itemInfo.value.archived) return false
|
||||||
|
if (isBookedIncomingInvoice.value) return !hasActiveBankAssignment.value
|
||||||
|
|
||||||
|
return mode.value !== "show"
|
||||||
|
})
|
||||||
const vendorName = computed(() => vendors.value.find((vendor) => vendor.id === itemInfo.value.vendor)?.name || "-")
|
const vendorName = computed(() => vendors.value.find((vendor) => vendor.id === itemInfo.value.vendor)?.name || "-")
|
||||||
const eInvoiceValidation = computed(() => itemInfo.value.eInvoiceValidation || null)
|
const eInvoiceValidation = computed(() => itemInfo.value.eInvoiceValidation || null)
|
||||||
const eInvoiceSourceLabel = computed(() => {
|
const eInvoiceSourceLabel = computed(() => {
|
||||||
@@ -248,7 +279,9 @@ const applyTotalTaxOverride = () => {
|
|||||||
|
|
||||||
// --- Saving ---
|
// --- Saving ---
|
||||||
const updateIncomingInvoice = async (setBooked = false) => {
|
const updateIncomingInvoice = async (setBooked = false) => {
|
||||||
if (setBooked && hasBlockingIncomingInvoiceErrors.value) {
|
const keepBooked = setBooked || isBookedIncomingInvoice.value
|
||||||
|
|
||||||
|
if (keepBooked && hasBlockingIncomingInvoiceErrors.value) {
|
||||||
toast.add({
|
toast.add({
|
||||||
title: "Buchen nicht möglich",
|
title: "Buchen nicht möglich",
|
||||||
description: "Bitte beheben Sie zuerst die rot markierten Pflichtfehler.",
|
description: "Bitte beheben Sie zuerst die rot markierten Pflichtfehler.",
|
||||||
@@ -260,7 +293,7 @@ const updateIncomingInvoice = async (setBooked = false) => {
|
|||||||
let item = { ...itemInfo.value }
|
let item = { ...itemInfo.value }
|
||||||
item.accounts = (item.accounts || []).map((account) => ensureDepreciationDefaults({ ...account }, item.date))
|
item.accounts = (item.accounts || []).map((account) => ensureDepreciationDefaults({ ...account }, item.date))
|
||||||
delete item.files
|
delete item.files
|
||||||
item.state = setBooked ? "Gebucht" : "Entwurf"
|
item.state = keepBooked ? "Gebucht" : "Entwurf"
|
||||||
|
|
||||||
await useEntities('incominginvoices').update(itemInfo.value.id, item, !setBooked)
|
await useEntities('incominginvoices').update(itemInfo.value.id, item, !setBooked)
|
||||||
|
|
||||||
@@ -321,8 +354,16 @@ const hasBlockingIncomingInvoiceErrors = computed(() => blockingIncomingInvoiceE
|
|||||||
</h1>
|
</h1>
|
||||||
</template>
|
</template>
|
||||||
<template #right>
|
<template #right>
|
||||||
|
<UButton
|
||||||
|
v-if="canEditIncomingInvoice"
|
||||||
|
icon="i-heroicons-pencil-square"
|
||||||
|
variant="outline"
|
||||||
|
@click="navigateTo(`/incomingInvoices/edit/${route.params.id}`)"
|
||||||
|
>
|
||||||
|
Bearbeiten
|
||||||
|
</UButton>
|
||||||
<ArchiveButton
|
<ArchiveButton
|
||||||
v-if="mode !== 'show'"
|
v-if="canArchiveIncomingInvoice"
|
||||||
color="error"
|
color="error"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
type="incominginvoices"
|
type="incominginvoices"
|
||||||
@@ -332,7 +373,7 @@ const hasBlockingIncomingInvoiceErrors = computed(() => blockingIncomingInvoiceE
|
|||||||
Speichern
|
Speichern
|
||||||
</UButton>
|
</UButton>
|
||||||
<UButton
|
<UButton
|
||||||
v-if="mode !== 'show'"
|
v-if="mode !== 'show' && !isBookedIncomingInvoice"
|
||||||
@click="updateIncomingInvoice(true)"
|
@click="updateIncomingInvoice(true)"
|
||||||
:disabled="hasBlockingIncomingInvoiceErrors"
|
:disabled="hasBlockingIncomingInvoiceErrors"
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user