feat(accounting): fehlende PDF von Ausgangsbelegen nacherzeugen

Auf der Belegansicht erscheint "PDF nacherzeugen", wenn ein
fertiggestellter Beleg kein angehängtes Dokument hat. Die PDF wird mit
derselben Logik wie beim Fertigstellen erzeugt und angehängt, ohne den
Datensatz zu speichern.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-27 20:40:25 +01:00
parent f814689ddd
commit 76e82eae4f
2 changed files with 93 additions and 33 deletions

View File

@@ -730,6 +730,12 @@ const setupPage = async () => {
await router.replace('/createDocument') await router.replace('/createDocument')
return return
} }
if (route.query.regeneratePdf && route.params.id) {
await regenerateMissingPdf()
return
}
loaded.value = true loaded.value = true
} }
@@ -1980,38 +1986,7 @@ const closeDocument = async () => {
await saveDocument("Gebucht") await saveDocument("Gebucht")
await generateDocument() await uploadGeneratedDocument()
let fileData = {}
fileData.project = itemInfo.value.project
fileData.createddocument = itemInfo.value.id
let mappedType = documentStorageFallbackTypes[itemInfo.value.type] || itemInfo.value.type
const folders = await useEntities("folders").select()
console.log(folders)
fileData.folder = folders.find(i => i.function === mappedType && i.year === Number(dayjs().format("YYYY"))).id
const tags = await useEntities("filetags").select()
console.log(tags)
fileData.type = tags.find(i => i.createdDocumentType === mappedType).id
function dataURLtoFile(dataurl, filename) {
var arr = dataurl.split(","),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[arr.length - 1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, {type: mime});
}
let file = dataURLtoFile(uri.value, `${itemInfo.value.documentNumber}.pdf`)
await useFiles().uploadFiles(fileData, [file])
guard.value = false guard.value = false
@@ -2021,6 +1996,77 @@ const closeDocument = async () => {
}
// Erzeugt die PDF aus dem aktuellen Stand und hängt sie an den Beleg an, ohne den Datensatz zu speichern
const uploadGeneratedDocument = async () => {
await generateDocument()
if (!uri.value) throw new Error("PDF konnte nicht erzeugt werden")
let fileData = {}
fileData.project = itemInfo.value.project
fileData.createddocument = itemInfo.value.id
let mappedType = documentStorageFallbackTypes[itemInfo.value.type] || itemInfo.value.type
const folders = await useEntities("folders").select()
console.log(folders)
fileData.folder = folders.find(i => i.function === mappedType && i.year === Number(dayjs().format("YYYY"))).id
const tags = await useEntities("filetags").select()
console.log(tags)
fileData.type = tags.find(i => i.createdDocumentType === mappedType).id
function dataURLtoFile(dataurl, filename) {
var arr = dataurl.split(","),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[arr.length - 1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, {type: mime});
}
let file = dataURLtoFile(uri.value, `${itemInfo.value.documentNumber}.pdf`)
await useFiles().uploadFiles(fileData, [file])
}
const regenerateMissingPdf = async () => {
guard.value = false
const showPath = `/createDocument/show/${route.params.id}`
try {
const document = await useEntities("createddocuments").selectSingle(route.params.id, "*,files(*)")
if (document?.state === "Entwurf" || document?.type === "serialInvoices") {
toast.add({ title: "PDF kann nur für fertiggestellte Belege nacherzeugt werden", color: "error" })
} else if ((document?.files || []).length > 0) {
toast.add({ title: "Am Beleg ist bereits ein Dokument angehängt", color: "warning" })
} else {
// Vertrags- und Mandats-Watcher überschreiben beim Laden die Zahlungsdaten, für die PDF gilt der gespeicherte Stand
const restoreStoredPaymentData = () => {
itemInfo.value.payment_type = document.payment_type
itemInfo.value.outgoingsepamandate = normalizeEntityId(document.outgoingsepamandate)
}
await nextTick()
restoreStoredPaymentData()
await nextTick()
restoreStoredPaymentData()
await uploadGeneratedDocument()
toast.add({ title: "PDF nacherzeugt", color: "success" })
}
} catch (error) {
console.error(error)
toast.add({ title: "PDF konnte nicht nacherzeugt werden", description: error?.data?.error || error?.message, color: "error" })
}
await router.replace(showPath)
} }
const getTextTemplateByType = (type, pos) => { const getTextTemplateByType = (type, pos) => {

View File

@@ -110,10 +110,18 @@ const setupPage = async () => {
links.value = createddocuments.filter(i => i.createddocument?.id === itemInfo.value.id) links.value = createddocuments.filter(i => i.createddocument?.id === itemInfo.value.id)
} }
linkedDocument.value = await useFiles().selectDocument(itemInfo.value.files[0].id) if (itemInfo.value.files?.length > 0) {
linkedDocument.value = await useFiles().selectDocument(itemInfo.value.files[0].id)
}
} }
} }
const canRegeneratePdf = computed(() => itemInfo.value.id
&& itemInfo.value.state !== 'Entwurf'
&& itemInfo.value.type !== 'serialInvoices'
&& Array.isArray(itemInfo.value.files)
&& itemInfo.value.files.length === 0)
setupPage() setupPage()
const getStatementLike = (allocation) => allocation?.bankstatement || (typeof allocation?.bs_id === "object" ? allocation.bs_id : null) const getStatementLike = (allocation) => allocation?.bankstatement || (typeof allocation?.bs_id === "object" ? allocation.bs_id : null)
@@ -282,6 +290,12 @@ const togglePortalRelease = async () => {
:loading="costCentresLoading" :loading="costCentresLoading"
@click="openCostCentres" @click="openCostCentres"
>Kostenstellen bearbeiten</UButton> >Kostenstellen bearbeiten</UButton>
<UButton
v-if="canRegeneratePdf"
icon="i-heroicons-document-arrow-down"
variant="outline"
@click="router.push(`/createDocument/edit/${itemInfo.id}?regeneratePdf=1`)"
>PDF nacherzeugen</UButton>
<!-- <UButton <!-- <UButton
:to="dataStore.documents.find(i => i.createdDocument === itemInfo.id) ? dataStore.documents.find(i => i.createdDocument === itemInfo.id).url : ''" :to="dataStore.documents.find(i => i.createdDocument === itemInfo.id) ? dataStore.documents.find(i => i.createdDocument === itemInfo.id).url : ''"
target="_blank" target="_blank"