diff --git a/pages/createDocument/edit/[[id]].vue b/pages/createDocument/edit/[[id]].vue index 7231861..dad37e4 100644 --- a/pages/createDocument/edit/[[id]].vue +++ b/pages/createDocument/edit/[[id]].vue @@ -632,7 +632,7 @@ const findDocumentErrors = computed(() => { if (itemInfo.value.customer === null) errors.push({message: "Es ist kein Kunde ausgewählt", type: "breaking"}) if (itemInfo.value.contact === null) errors.push({message: "Es ist kein Kontakt ausgewählt", type: "info"}) if (itemInfo.value.letterhead === null) errors.push({message: "Es ist kein Briefpapier ausgewählt", type: "breaking"}) - if (itemInfo.value.created_by === null || !itemInfo.value.created_by) errors.push({message: "Es ist kein Ansprechpartner im Unternehmen ausgewählt", type: "breaking"}) + if (itemInfo.value.created_by === null || !itemInfo.value.created_by) errors.push({message: "Es ist kein Mitarbeiter ausgewählt", type: "breaking"}) if (itemInfo.value.address.street === null) errors.push({ message: "Es ist keine Straße im Adressat angegeben", type: "breaking" @@ -2028,7 +2028,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = { Monatlich Quartalsweise + @@ -153,10 +157,40 @@ +
+ + +
+ + + +
+
+
@@ -169,6 +203,9 @@ +
@@ -249,19 +286,16 @@ const showExecutionModal = ref(false) const executionDate = ref(dayjs().format('YYYY-MM-DD')) const selectedExecutionRows = ref([]) const isExecuting = ref(false) +const modalSearch = ref("") // NEU: Suchstring für das Modal -// --- SerialExecutions State (Liste der Ausführungen) --- +// --- SerialExecutions State --- const showExecutionsSlideover = ref(false) const executionItems = ref([]) const executionsLoading = ref(false) -// NEU: Statusvariable für den Fertigstellungs-Prozess const finishingId = ref(null) const setupPage = async () => { - // 1. Vorlagen laden items.value = await useEntities("createddocuments").select("*, customer(id,name), contract(id,name, contractNumber)","documentDate",undefined,true) - - // 2. Ausführungen laden await fetchExecutions() } @@ -280,41 +314,30 @@ const fetchExecutions = async () => { } const runningExecutions = computed(() => { - // Filtert nach Status 'draft' return executionItems.value.filter(i => i.status === 'draft') }) const completedExecutions = computed(() => { - // Filtert alles was NICHT läuft (und nicht 'draft' ist, um Dopplungen zu vermeiden) return executionItems.value.filter(i => i.status !== 'running' && i.status !== 'pending' && i.status !== 'draft') }) const openExecutionsSlideover = () => { showExecutionsSlideover.value = true - fetchExecutions() // Refresh beim Öffnen + fetchExecutions() } -// NEU: Funktion zum Fertigstellen const finishExecution = async (executionId) => { if (!executionId) return - finishingId.value = executionId - try { - await $api(`/api/functions/serial/finish/${executionId}`, { - method: 'POST' - }) - + await $api(`/api/functions/serial/finish/${executionId}`, { method: 'POST' }) toast.add({ title: 'Ausführung beendet', description: 'Der Prozess wurde erfolgreich als fertig markiert.', icon: 'i-heroicons-check-circle', color: 'green' }) - - // Liste aktualisieren, damit die Karte aus "Laufend" verschwindet await fetchExecutions() - } catch (error) { console.error(error) toast.add({ @@ -330,28 +353,20 @@ const finishExecution = async (executionId) => { const getStatusColor = (status) => { switch (status) { - case 'completed': - return 'green' - case 'error': - return 'red' + case 'completed': return 'green' + case 'error': return 'red' case 'running': - case 'draft': // Draft auch als Primary färben - return 'primary' - default: - return 'gray' + case 'draft': return 'primary' + default: return 'gray' } } const getStatusLabel = (status) => { switch (status) { - case 'completed': - return 'Abgeschlossen' - case 'error': - return 'Fehlerhaft' - case 'draft': - return 'Gestartet' - default: - return status + case 'completed': return 'Abgeschlossen' + case 'error': return 'Fehlerhaft' + case 'draft': return 'Gestartet' + default: return status } } @@ -375,16 +390,39 @@ const filteredRows = computed(() => { return useSearch(searchString.value, temp.slice().reverse()) }) +// Basis Liste für das Modal (nur Aktive) const activeTemplates = computed(() => { return items.value - .filter(i => i.type === "serialInvoices" && i.serialConfig?.active === true) + .filter(i => i.type === "serialInvoices" && !!i.serialConfig?.active) .map(i => ({...i})) }) -// NEU: Schnellaktionen Logik +// NEU: Gefilterte Liste für das Modal basierend auf der Suche +const filteredExecutionList = computed(() => { + if (!modalSearch.value) return activeTemplates.value + + const term = modalSearch.value.toLowerCase() + + return activeTemplates.value.filter(row => { + const customerName = row.customer?.name?.toLowerCase() || "" + const contractNum = row.contract?.contractNumber?.toLowerCase() || "" + const contractName = row.contract?.name?.toLowerCase() || "" + + return customerName.includes(term) || + contractNum.includes(term) || + contractName.includes(term) + }) +}) + +// NEU: Alle auswählen (nur die aktuell sichtbaren/gefilterten) +const selectAllTemplates = () => { + // WICHTIG: Überschreibt nicht bestehende Auswahl, sondern fügt hinzu oder ersetzt. + // Hier ersetzen wir die Auswahl komplett mit dem aktuellen Filterergebnis + selectedExecutionRows.value = [...filteredExecutionList.value] +} + const getActionItems = (row) => { const isActive = row.serialConfig && row.serialConfig.active - return [ [{ label: isActive ? 'Deaktivieren' : 'Aktivieren', @@ -397,19 +435,11 @@ const getActionItems = (row) => { const toggleActiveState = async (row) => { const newState = !row.serialConfig.active - - // Optimistisches Update im UI row.serialConfig.active = newState - try { - // Annahme: createddocuments Tabelle erlaubt update await useEntities('createddocuments').update(row.id, { - serialConfig: { - ...row.serialConfig, - active: newState - } + serialConfig: { ...row.serialConfig, active: newState } }) - toast.add({ title: newState ? 'Aktiviert' : 'Deaktiviert', description: `Die Vorlage wurde ${newState ? 'aktiviert' : 'deaktiviert'}.`, @@ -418,7 +448,6 @@ const toggleActiveState = async (row) => { }) } catch (e) { console.error(e) - // Rollback im Fehlerfall row.serialConfig.active = !newState toast.add({ title: 'Fehler', @@ -429,12 +458,13 @@ const toggleActiveState = async (row) => { } const templateColumns = [ - { key: 'actions', label: '' }, // NEU: Spalte für Menü ganz links + { key: 'actions', label: '' }, { key: 'serialConfig.active', label: "Aktiv" }, { key: "amount", label: "Betrag" }, { key: 'partner', label: "Kunde" }, { key: 'contract', label: "Vertrag" }, - { key: 'serialConfig.intervall', label: "Rhythmus" } + { key: 'serialConfig.intervall', label: "Rhythmus" }, + { key: 'payment_type', label: "Zahlart" } ] const executionColumns = [ @@ -480,6 +510,7 @@ const calculateDocSum = (row) => { const openExecutionModal = () => { executionDate.value = dayjs().format('YYYY-MM-DD') selectedExecutionRows.value = [] + modalSearch.value = "" // Reset Search showExecutionModal.value = true } @@ -510,7 +541,6 @@ const executeSerialInvoices = async () => { showExecutionModal.value = false selectedExecutionRows.value = [] - // Liste aktualisieren, um den "Läuft" Status zu sehen await fetchExecutions() } catch (error) { diff --git a/pages/incomingInvoices/index.vue b/pages/incomingInvoices/index.vue index 8d12d6e..52e884f 100644 --- a/pages/incomingInvoices/index.vue +++ b/pages/incomingInvoices/index.vue @@ -2,6 +2,9 @@ import dayjs from "dayjs" import {useSum} from "~/composables/useSum.js"; +// Zugriff auf API und Toast +const { $api } = useNuxtApp() +const toast = useToast() defineShortcuts({ '/': () => { @@ -47,6 +50,9 @@ const sort = ref({ direction: 'desc' }) +// Status für den Button +const isPreparing = ref(false) + const type = "incominginvoices" const dataType = dataStore.dataTypes[type] @@ -54,6 +60,34 @@ const setupPage = async () => { items.value = await useEntities(type).select("*, vendor(id,name), statementallocations(id,amount)",sort.value.column,sort.value.direction === "asc") } +// Funktion zum Vorbereiten der Belege +const prepareInvoices = async () => { + isPreparing.value = true + try { + await $api('/api/functions/services/prepareincominginvoices', { method: 'POST' }) + + toast.add({ + title: 'Erfolg', + description: 'Eingangsbelege wurden vorbereitet.', + icon: 'i-heroicons-check-circle', + color: 'green' + }) + + // Liste neu laden + await setupPage() + } catch (error) { + console.error(error) + toast.add({ + title: 'Fehler', + description: 'Beim Vorbereiten der Belege ist ein Fehler aufgetreten.', + icon: 'i-heroicons-exclamation-circle', + color: 'red' + }) + } finally { + isPreparing.value = false + } +} + setupPage() const selectedColumns = ref(tempStore.columns[type] ? tempStore.columns[type] : dataType.templateColumns.filter(i => !i.disabledInTable)) @@ -128,6 +162,17 @@ const selectIncomingInvoice = (invoice) => {