Passt Kassenbuch Ansicht an
Kassenbücher werden nun zuerst tabellarisch angezeigt. Das Erstellen einer Barkasse erfolgt über ein Modal und einzelne Kassenbücher öffnen sich über eine Detailseite.
This commit is contained in:
165
frontend/pages/accounting/cashbooks/index.vue
Normal file
165
frontend/pages/accounting/cashbooks/index.vue
Normal file
@@ -0,0 +1,165 @@
|
||||
<script setup>
|
||||
const toast = useToast()
|
||||
const router = useRouter()
|
||||
const tempStore = useTempStore()
|
||||
|
||||
const loading = ref(true)
|
||||
const savingCashbook = ref(false)
|
||||
const createCashbookModalOpen = ref(false)
|
||||
const cashbooks = ref([])
|
||||
const searchString = ref(tempStore.searchStrings["cashbooks"] || "")
|
||||
|
||||
const newCashbook = reactive({
|
||||
name: "",
|
||||
datevNumber: "1000",
|
||||
openingBalance: 0
|
||||
})
|
||||
|
||||
const displayCurrency = (value) => `${Number(value || 0).toFixed(2).replace(".", ",")} €`
|
||||
|
||||
const templateColumns = [
|
||||
{
|
||||
key: "name",
|
||||
label: "Bezeichnung"
|
||||
},
|
||||
{
|
||||
key: "datevNumber",
|
||||
label: "Kontennummer"
|
||||
},
|
||||
{
|
||||
key: "balance",
|
||||
label: "Anfangsbestand"
|
||||
},
|
||||
{
|
||||
key: "syncedAt",
|
||||
label: "Erstellt"
|
||||
}
|
||||
]
|
||||
|
||||
const filteredRows = computed(() => useSearch(searchString.value, cashbooks.value))
|
||||
|
||||
const clearSearchString = () => {
|
||||
tempStore.clearSearchString("cashbooks")
|
||||
searchString.value = ""
|
||||
}
|
||||
|
||||
const loadCashbooks = async () => {
|
||||
loading.value = true
|
||||
cashbooks.value = await useNuxtApp().$api("/api/banking/cashbooks")
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
const resetCreateForm = () => {
|
||||
newCashbook.name = ""
|
||||
newCashbook.datevNumber = "1000"
|
||||
newCashbook.openingBalance = 0
|
||||
}
|
||||
|
||||
const createCashbook = async () => {
|
||||
if (!newCashbook.name || !newCashbook.datevNumber) {
|
||||
toast.add({ title: "Bitte Bezeichnung und Kontennummer ausfüllen.", color: "warning" })
|
||||
return
|
||||
}
|
||||
|
||||
savingCashbook.value = true
|
||||
try {
|
||||
const created = await useNuxtApp().$api("/api/banking/cashbooks", {
|
||||
method: "POST",
|
||||
body: {
|
||||
name: newCashbook.name,
|
||||
datevNumber: newCashbook.datevNumber,
|
||||
openingBalance: Number(newCashbook.openingBalance || 0)
|
||||
}
|
||||
})
|
||||
toast.add({ title: "Barkasse erstellt." })
|
||||
createCashbookModalOpen.value = false
|
||||
resetCreateForm()
|
||||
await loadCashbooks()
|
||||
router.push(`/accounting/cashbooks/${created.id}`)
|
||||
} finally {
|
||||
savingCashbook.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(loadCashbooks)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UDashboardPanelContent>
|
||||
<UDashboardNavbar title="Kassenbücher" :badge="filteredRows.length">
|
||||
<template #right>
|
||||
<UInput
|
||||
id="searchinput"
|
||||
v-model="searchString"
|
||||
icon="i-heroicons-magnifying-glass"
|
||||
autocomplete="off"
|
||||
placeholder="Suche..."
|
||||
class="hidden lg:block"
|
||||
@keydown.esc="$event.target.blur()"
|
||||
@change="tempStore.modifySearchString('cashbooks', searchString)"
|
||||
/>
|
||||
<UButton
|
||||
v-if="searchString.length > 0"
|
||||
icon="i-heroicons-x-mark"
|
||||
variant="outline"
|
||||
color="error"
|
||||
@click="clearSearchString"
|
||||
/>
|
||||
<UButton icon="i-heroicons-plus" @click="createCashbookModalOpen = true">
|
||||
Barkasse
|
||||
</UButton>
|
||||
</template>
|
||||
</UDashboardNavbar>
|
||||
|
||||
<UTable
|
||||
:data="filteredRows"
|
||||
:columns="normalizeTableColumns(templateColumns)"
|
||||
:loading="loading"
|
||||
class="w-full"
|
||||
:ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }"
|
||||
:on-select="(row) => router.push(`/accounting/cashbooks/${row.original?.id || row.id}`)"
|
||||
:empty="{ icon: 'i-heroicons-banknotes', label: 'Keine Kassenbücher angelegt' }"
|
||||
>
|
||||
<template #datevNumber-cell="{ row }">
|
||||
<span class="font-mono">{{ row.original.datevNumber }}</span>
|
||||
</template>
|
||||
<template #balance-cell="{ row }">
|
||||
{{ displayCurrency(row.original.balance) }}
|
||||
</template>
|
||||
<template #syncedAt-cell="{ row }">
|
||||
{{ row.original.createdAt ? new Date(row.original.createdAt).toLocaleDateString("de-DE") : "-" }}
|
||||
</template>
|
||||
</UTable>
|
||||
|
||||
<UModal v-model:open="createCashbookModalOpen">
|
||||
<template #content>
|
||||
<UCard>
|
||||
<template #header>
|
||||
<div class="text-lg font-semibold">Barkasse anlegen</div>
|
||||
</template>
|
||||
|
||||
<UForm :state="newCashbook" class="space-y-4" @submit.prevent="createCashbook">
|
||||
<UFormField label="Bezeichnung">
|
||||
<UInput v-model="newCashbook.name" placeholder="z. B. Hauptkasse" />
|
||||
</UFormField>
|
||||
<UFormField label="Kontennummer">
|
||||
<UInput v-model="newCashbook.datevNumber" placeholder="1000" />
|
||||
</UFormField>
|
||||
<UFormField label="Anfangsbestand">
|
||||
<UInput v-model="newCashbook.openingBalance" type="number" step="0.01" />
|
||||
</UFormField>
|
||||
|
||||
<div class="flex justify-end gap-3 pt-2">
|
||||
<UButton color="gray" variant="soft" @click="createCashbookModalOpen = false">
|
||||
Abbrechen
|
||||
</UButton>
|
||||
<UButton type="submit" color="primary" :loading="savingCashbook">
|
||||
Barkasse anlegen
|
||||
</UButton>
|
||||
</div>
|
||||
</UForm>
|
||||
</UCard>
|
||||
</template>
|
||||
</UModal>
|
||||
</UDashboardPanelContent>
|
||||
</template>
|
||||
Reference in New Issue
Block a user