feat(accounting): allow cancelling serial invoice executions
This commit is contained in:
@@ -28,6 +28,7 @@
|
||||
color="white"
|
||||
variant="solid"
|
||||
label="Ausführen"
|
||||
:loading="isExecuting"
|
||||
@click="openExecutionModal"
|
||||
/>
|
||||
|
||||
@@ -70,21 +71,33 @@
|
||||
<div>
|
||||
<p class="text-sm font-medium">Start: {{ dayjs(exec.createdAt).format('DD.MM.YYYY HH:mm') }}</p>
|
||||
</div>
|
||||
<UBadge color="primary" variant="subtle">Läuft</UBadge>
|
||||
<UBadge :color="getStatusColor(exec.status)" variant="subtle">{{ getStatusLabel(exec.status) }}</UBadge>
|
||||
</div>
|
||||
|
||||
<div class="text-xs text-gray-500 flex justify-between mb-3">
|
||||
<span>{{exec.summary}}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end pt-2 border-t border-gray-100 dark:border-gray-800">
|
||||
<div class="flex justify-end gap-2 pt-2 border-t border-gray-100 dark:border-gray-800">
|
||||
<UButton
|
||||
size="xs"
|
||||
color="error"
|
||||
variant="soft"
|
||||
icon="i-heroicons-stop"
|
||||
label="Durchlauf abbrechen"
|
||||
:loading="cancellingId === exec.id"
|
||||
:disabled="exec.status === 'cancelling' || cancellingId !== null"
|
||||
@click="cancelExecution(exec.id)"
|
||||
/>
|
||||
<UButton
|
||||
v-if="['draft', 'error'].includes(exec.status)"
|
||||
size="xs"
|
||||
color="white"
|
||||
variant="solid"
|
||||
icon="i-heroicons-check"
|
||||
label="Fertigstellen"
|
||||
:loading="finishingId === exec.id"
|
||||
:disabled="finishingId !== null || cancellingId !== null"
|
||||
@click="finishExecution(exec.id)"
|
||||
/>
|
||||
</div>
|
||||
@@ -343,6 +356,14 @@ const showExecutionsSlideover = ref(false)
|
||||
const executionItems = ref([])
|
||||
const executionsLoading = ref(false)
|
||||
const finishingId = ref(null)
|
||||
const cancellingId = ref(null)
|
||||
let executionsPoll
|
||||
onMounted(() => {
|
||||
executionsPoll = setInterval(() => {
|
||||
if (!executionsLoading.value) fetchExecutions()
|
||||
}, 3000)
|
||||
})
|
||||
onUnmounted(() => clearInterval(executionsPoll))
|
||||
|
||||
const setupPage = async () => {
|
||||
items.value = await useEntities("createddocuments").select("*, customer(id,name), contract(id,name, contractNumber), plant(id,name)","documentDate",undefined,true)
|
||||
@@ -364,11 +385,11 @@ const fetchExecutions = async () => {
|
||||
}
|
||||
|
||||
const runningExecutions = computed(() => {
|
||||
return executionItems.value.filter(i => i.status === 'draft')
|
||||
return executionItems.value.filter(i => ['generating', 'draft', 'processing', 'cancelling', 'error'].includes(i.status))
|
||||
})
|
||||
|
||||
const completedExecutions = computed(() => {
|
||||
return executionItems.value.filter(i => i.status !== 'running' && i.status !== 'pending' && i.status !== 'draft')
|
||||
return executionItems.value.filter(i => ['completed', 'cancelled'].includes(i.status))
|
||||
})
|
||||
|
||||
const openExecutionsSlideover = () => {
|
||||
@@ -376,16 +397,34 @@ const openExecutionsSlideover = () => {
|
||||
fetchExecutions()
|
||||
}
|
||||
|
||||
const cancelExecution = async (executionId) => {
|
||||
if (cancellingId.value || !window.confirm('Durchlauf abbrechen? Ungebuchte Rechnungsentwürfe werden archiviert. Bereits gebuchte Rechnungen bleiben erhalten.')) return
|
||||
cancellingId.value = executionId
|
||||
try {
|
||||
const result = await $api(`/api/functions/serial/cancel/${executionId}`, { method: 'POST' })
|
||||
toast.add({
|
||||
title: result.execution.status === 'cancelling' ? 'Abbruch angefordert' : 'Durchlauf abgebrochen',
|
||||
description: result.execution.status === 'cancelling' ? 'Die gerade bearbeitete Rechnung wird noch abgeschlossen.' : 'Ungebuchte Rechnungsentwürfe wurden archiviert.',
|
||||
color: 'success'
|
||||
})
|
||||
await fetchExecutions()
|
||||
} catch (error) {
|
||||
toast.add({ title: 'Abbruch fehlgeschlagen', description: error.data?.message || 'Der Durchlauf konnte nicht abgebrochen werden.', color: 'error' })
|
||||
} finally {
|
||||
cancellingId.value = null
|
||||
}
|
||||
}
|
||||
|
||||
const finishExecution = async (executionId) => {
|
||||
if (!executionId) return
|
||||
finishingId.value = executionId
|
||||
try {
|
||||
await $api(`/api/functions/serial/finish/${executionId}`, { method: 'POST' })
|
||||
const result = await $api(`/api/functions/serial/finish/${executionId}`, { method: 'POST' })
|
||||
toast.add({
|
||||
title: 'Ausführung beendet',
|
||||
description: 'Der Prozess wurde erfolgreich als fertig markiert.',
|
||||
title: getStatusLabel(result.execution.status),
|
||||
description: result.execution.summary,
|
||||
icon: 'i-heroicons-check-circle',
|
||||
color: 'green'
|
||||
color: result.errors > 0 ? 'error' : 'success'
|
||||
})
|
||||
await fetchExecutions()
|
||||
} catch (error) {
|
||||
@@ -403,11 +442,13 @@ const finishExecution = async (executionId) => {
|
||||
|
||||
const getStatusColor = (status) => {
|
||||
switch (status) {
|
||||
case 'completed': return 'green'
|
||||
case 'error': return 'red'
|
||||
case 'completed': return 'success'
|
||||
case 'error': return 'error'
|
||||
case 'generating':
|
||||
case 'processing':
|
||||
case 'running':
|
||||
case 'draft': return 'primary'
|
||||
default: return 'gray'
|
||||
default: return 'neutral'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,7 +456,11 @@ const getStatusLabel = (status) => {
|
||||
switch (status) {
|
||||
case 'completed': return 'Abgeschlossen'
|
||||
case 'error': return 'Fehlerhaft'
|
||||
case 'draft': return 'Gestartet'
|
||||
case 'draft': return 'Vorbereitet'
|
||||
case 'generating': return 'Wird vorbereitet'
|
||||
case 'processing': return 'Wird fertiggestellt'
|
||||
case 'cancelling': return 'Wird abgebrochen'
|
||||
case 'cancelled': return 'Abgebrochen'
|
||||
default: return status
|
||||
}
|
||||
}
|
||||
@@ -645,14 +690,15 @@ const executeSerialInvoices = async () => {
|
||||
templateIds: selectedExecutionRows.value.map(row => row.id)
|
||||
}
|
||||
|
||||
showExecutionModal.value = false
|
||||
const res = await $api('/api/functions/serial/start', {
|
||||
method: 'POST',
|
||||
body: payload
|
||||
})
|
||||
|
||||
toast.add({
|
||||
title: 'Ausführung gestartet',
|
||||
description: `${res.results.length} Rechnungen wurden für den Lauf vorbereitet.`,
|
||||
title: res.execution.status === 'cancelled' ? 'Durchlauf abgebrochen' : 'Ausführung gestartet',
|
||||
description: res.execution.status === 'cancelled' ? res.execution.summary : `${res.results.filter(result => result.status === 'success').length} Rechnungen wurden für den Lauf vorbereitet.`,
|
||||
icon: 'i-heroicons-check-circle',
|
||||
color: 'green'
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user