Added Vehicles
Added Bankimport, BankAccounts, BankStatements Some Visual Changes Added Contacts Changes in VendorInvoices Added layouts with default an one for Login PAge Added Input Group Component
This commit is contained in:
6
docu
6
docu
@@ -25,3 +25,9 @@ Admin Client:
|
|||||||
Client Id: 7d1d57c6-6368-4aea-811d-9e651fd55a36
|
Client Id: 7d1d57c6-6368-4aea-811d-9e651fd55a36
|
||||||
Client Secret: 30579e3c-8fbb-4684-a344-a521f962fd7c
|
Client Secret: 30579e3c-8fbb-4684-a344-a521f962fd7c
|
||||||
Data Decryption Key: 3598063141d04b3b97c60c15fe4c9099
|
Data Decryption Key: 3598063141d04b3b97c60c15fe4c9099
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
GoCardless:
|
||||||
|
Sec: 65a3ffe63e403a19241ddd8f316566cb3f41e3fa065946ab582f881e16e8bed0f1b86ee29667950c88a43dcfb1c71d27049ee686a777f85d86383df28664ec55
|
||||||
|
Key: 478cba26-f265-4e17-b8a8-679e0e8a855b
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const colorMode = useColorMode()
|
|||||||
const supabase = useSupabaseClient()
|
const supabase = useSupabaseClient()
|
||||||
const tenants = (await supabase.from("tenants").select()).data
|
const tenants = (await supabase.from("tenants").select()).data
|
||||||
const {loaded, profiles} = storeToRefs(useDataStore())
|
const {loaded, profiles} = storeToRefs(useDataStore())
|
||||||
const {fetchData, getProfileById} = useDataStore()
|
const {fetchData, getProfileById, clearStore} = useDataStore()
|
||||||
const userProfile = (user.value ? getProfileById(user.value.id) : {})
|
const userProfile = (user.value ? getProfileById(user.value.id) : {})
|
||||||
//console.log(userProfile)
|
//console.log(userProfile)
|
||||||
|
|
||||||
@@ -43,6 +43,11 @@ const navLinks = [
|
|||||||
to: "/vendors",
|
to: "/vendors",
|
||||||
icon: "i-heroicons-truck"
|
icon: "i-heroicons-truck"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "Kontakte",
|
||||||
|
to: "/contacts",
|
||||||
|
icon: "i-heroicons-user-group"
|
||||||
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -54,6 +59,11 @@ const navLinks = [
|
|||||||
to: "/vendorinvoices",
|
to: "/vendorinvoices",
|
||||||
icon: "i-heroicons-user-group"
|
icon: "i-heroicons-user-group"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "Bank",
|
||||||
|
to: "/banking",
|
||||||
|
icon: "i-heroicons-currency-euro"
|
||||||
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -128,6 +138,11 @@ const navLinks = [
|
|||||||
to: "/inventory/spaces",
|
to: "/inventory/spaces",
|
||||||
icon: "i-heroicons-square-3-stack-3d"
|
icon: "i-heroicons-square-3-stack-3d"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "Fahrzeuge",
|
||||||
|
to: "/vehicles",
|
||||||
|
icon: "i-heroicons-truck"
|
||||||
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@@ -201,8 +216,9 @@ const items = [
|
|||||||
icon: 'i-heroicons-cog-8-tooth',
|
icon: 'i-heroicons-cog-8-tooth',
|
||||||
to: "/settings/externalDevices"
|
to: "/settings/externalDevices"
|
||||||
},{
|
},{
|
||||||
label: 'Settings',
|
label: 'Benutzer',
|
||||||
icon: 'i-heroicons-cog-8-tooth'
|
icon: 'i-heroicons-user-group',
|
||||||
|
to: "/settings/users"
|
||||||
}], /*[{
|
}], /*[{
|
||||||
label: 'Documentation',
|
label: 'Documentation',
|
||||||
icon: 'i-heroicons-book-open'
|
icon: 'i-heroicons-book-open'
|
||||||
@@ -215,9 +231,11 @@ const items = [
|
|||||||
}],*/ [{
|
}],*/ [{
|
||||||
label: 'Sign out',
|
label: 'Sign out',
|
||||||
icon: 'i-heroicons-arrow-left-on-rectangle',
|
icon: 'i-heroicons-arrow-left-on-rectangle',
|
||||||
click: () => {
|
click: async () => {
|
||||||
supabase.auth.signOut()
|
await supabase.auth.signOut()
|
||||||
router.push("/login")
|
await clearStore()
|
||||||
|
await router.push("/login")
|
||||||
|
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
]
|
]
|
||||||
@@ -251,6 +269,7 @@ const items = [
|
|||||||
<UDropdown :items="items" :ui="{ item: { disabled: 'cursor-text select-text' } }" :popper="{ placement: 'bottom-start' }">
|
<UDropdown :items="items" :ui="{ item: { disabled: 'cursor-text select-text' } }" :popper="{ placement: 'bottom-start' }">
|
||||||
<UAvatar
|
<UAvatar
|
||||||
:alt="userProfile ? userProfile.firstName + ' ' + userProfile.lastName : '' "
|
:alt="userProfile ? userProfile.firstName + ' ' + userProfile.lastName : '' "
|
||||||
|
icon="i-heroicons-photo"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<template #account="{ item }">
|
<template #account="{ item }">
|
||||||
@@ -274,14 +293,11 @@ const items = [
|
|||||||
</UHeader>
|
</UHeader>
|
||||||
<UDivider />
|
<UDivider />
|
||||||
<div class="m-3" id="contentContainer">
|
<div class="m-3" id="contentContainer">
|
||||||
<NuxtPage
|
<NuxtLayout>
|
||||||
v-if="loaded"
|
<NuxtPage/>
|
||||||
/>
|
</NuxtLayout>
|
||||||
<div
|
|
||||||
v-else
|
|
||||||
>
|
|
||||||
<UProgress animation="carousel" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<!-- <UFooter>
|
<!-- <UFooter>
|
||||||
<template #left>
|
<template #left>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ const props = defineProps({
|
|||||||
|
|
||||||
const {document, openShowModal:openShowModalProp } = props;
|
const {document, openShowModal:openShowModalProp } = props;
|
||||||
const {fetchDocuments, getDocumentTags, fetchVendorInvoices} = useDataStore()
|
const {fetchDocuments, getDocumentTags, fetchVendorInvoices} = useDataStore()
|
||||||
const {projects, customers} = storeToRefs(useDataStore())
|
const {projects, customers, vendorInvoices} = storeToRefs(useDataStore())
|
||||||
const tags = getDocumentTags
|
const tags = getDocumentTags
|
||||||
const openShowModal = ref(false)
|
const openShowModal = ref(false)
|
||||||
|
|
||||||
@@ -103,15 +103,35 @@ const updateDocument = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const createVendorInvoice = async () => {
|
const createVendorInvoice = async () => {
|
||||||
const {data,error} = await supabase
|
const {data:vendorInvoiceData,error:vendorInvoiceError} = await supabase
|
||||||
.from("vendorInvoices")
|
.from("vendorInvoices")
|
||||||
.insert([{
|
.insert([{
|
||||||
document: document.id,
|
document: document.id,
|
||||||
}])
|
}])
|
||||||
.select()
|
.select()
|
||||||
if(error) {
|
if(vendorInvoiceError) {
|
||||||
console.log(error)
|
console.log(vendorInvoiceError)
|
||||||
} else if(data) {
|
} else if(vendorInvoiceData) {
|
||||||
|
|
||||||
|
const {data:documentData,error:documentError} = await supabase
|
||||||
|
.from("documents")
|
||||||
|
.update({
|
||||||
|
vendorInvoice: vendorInvoiceData[0].id
|
||||||
|
})
|
||||||
|
.eq('id',document.id)
|
||||||
|
.select()
|
||||||
|
|
||||||
|
if(documentError) {
|
||||||
|
console.log(documentError)
|
||||||
|
} else {
|
||||||
|
toast.add({title: "Dokument aktualisiert"})
|
||||||
|
fetchDocuments()
|
||||||
|
openShowModal.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fetchVendorInvoices()
|
fetchVendorInvoices()
|
||||||
await router.push("/vendorinvoices")
|
await router.push("/vendorinvoices")
|
||||||
}
|
}
|
||||||
@@ -135,6 +155,10 @@ const createVendorInvoice = async () => {
|
|||||||
v-model="document.selected"
|
v-model="document.selected"
|
||||||
class="ml-2"
|
class="ml-2"
|
||||||
/>
|
/>
|
||||||
|
<br>
|
||||||
|
<UBadge
|
||||||
|
v-if="document.vendorInvoice"
|
||||||
|
>{{vendorInvoices.find(item => item.id === document.vendorInvoice) ? vendorInvoices.find(item => item.id === document.vendorInvoice).reference : ''}}</UBadge>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@@ -226,7 +250,7 @@ const createVendorInvoice = async () => {
|
|||||||
.documentListItem {
|
.documentListItem {
|
||||||
display:block;
|
display:block;
|
||||||
width: 15vw;
|
width: 15vw;
|
||||||
height: 30vh;
|
height: 33vh;
|
||||||
padding:1em;
|
padding:1em;
|
||||||
margin: 0.7em;
|
margin: 0.7em;
|
||||||
border: 1px solid lightgrey;
|
border: 1px solid lightgrey;
|
||||||
|
|||||||
20
spaces/components/InputGroup.vue
Normal file
20
spaces/components/InputGroup.vue
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
gap: {
|
||||||
|
type: Number,
|
||||||
|
default: 1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const {gap} = props
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="`flex items-center gap-${gap}`">
|
||||||
|
<slot/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
21
spaces/layouts/default.vue
Normal file
21
spaces/layouts/default.vue
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<script setup>
|
||||||
|
const {loaded, profiles} = storeToRefs(useDataStore())
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
|
||||||
|
|
||||||
|
<slot v-if="loaded"/>
|
||||||
|
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
>
|
||||||
|
<UProgress animation="carousel" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
11
spaces/layouts/notLoggedIn.vue
Normal file
11
spaces/layouts/notLoggedIn.vue
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<slot/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
const route = useRoute()
|
|
||||||
const supabase = useSupabaseClient()
|
|
||||||
console.log(route.params.account)
|
|
||||||
|
|
||||||
const accounts = (await supabase.from("bankAccounts").select()).data
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@@ -1,103 +1,88 @@
|
|||||||
<script setup>
|
|
||||||
import Papa from 'papaparse'
|
|
||||||
|
|
||||||
const supabase = useSupabaseClient()
|
|
||||||
|
|
||||||
const accounts = (supabase.from("bankAccounts").select()).data
|
|
||||||
|
|
||||||
let items = ref([])
|
|
||||||
async function readSingleFile(evt) {
|
|
||||||
var f = document.getElementById('fileInput').files[0];
|
|
||||||
if (f) {
|
|
||||||
let results = []
|
|
||||||
let text = await f.text()
|
|
||||||
results = Papa.parse(text).data
|
|
||||||
console.log(results[0])
|
|
||||||
results = results.slice(1)
|
|
||||||
results = results.map(temp => {
|
|
||||||
return {
|
|
||||||
iban: temp[1],
|
|
||||||
bank: temp[3],
|
|
||||||
date: temp[4],
|
|
||||||
partnerName: temp[6],
|
|
||||||
partnerIban: temp[7],
|
|
||||||
type: temp[9],
|
|
||||||
text: temp[10],
|
|
||||||
value: temp[11],
|
|
||||||
currency: temp[12]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
console.log(results)
|
|
||||||
items.value = results
|
|
||||||
|
|
||||||
parseStatements()
|
|
||||||
|
|
||||||
} else {
|
|
||||||
alert("Failed to load file");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const newStatements = ref([])
|
|
||||||
|
|
||||||
const parseStatements = async () => {
|
|
||||||
items.value.forEach(item => {
|
|
||||||
if(item.date) {
|
|
||||||
let returnObj = {
|
|
||||||
date: item.date,
|
|
||||||
partnerIban: item.partnerIban,
|
|
||||||
partnerName: item.partnerName,
|
|
||||||
text: item.text,
|
|
||||||
value: Number(item.value.replace(",",".")),
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
newStatements.value.push(returnObj)
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
const {data,error} = await supabase
|
|
||||||
.from("bankStatements")
|
|
||||||
.insert(newStatements.value)
|
|
||||||
.select()
|
|
||||||
|
|
||||||
console.log(data)
|
|
||||||
console.log(error)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div id="main">
|
||||||
<UInput
|
<!-- TODO: Kontakte erstellen und dem Kunden zuweisen -->
|
||||||
type="file"
|
|
||||||
id="fileInput"
|
|
||||||
/>
|
|
||||||
<UButton
|
|
||||||
@click="readSingleFile"
|
|
||||||
>Test</UButton>
|
|
||||||
|
|
||||||
<table>
|
<!--<InputGroup>
|
||||||
<tr
|
<UButton @click="router.push(`/banking/accounts/create/`)">+ Kontakt</UButton>
|
||||||
v-for="temp in items"
|
|
||||||
|
<UInput
|
||||||
|
v-model="searchString"
|
||||||
|
placeholder="Suche..."
|
||||||
|
/>
|
||||||
|
</InputGroup>-->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<UTable
|
||||||
|
:rows="filteredRows"
|
||||||
|
:columns="itemColumns"
|
||||||
|
@select="selectItem"
|
||||||
|
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
|
||||||
>
|
>
|
||||||
<td>{{temp.date}}</td>
|
|
||||||
<td>{{temp.partnerName}}</td>
|
|
||||||
<td>{{temp.text}}</td>
|
|
||||||
<td>{{temp.value}}</td>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
|
</UTable>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
|
||||||
|
definePageMeta({
|
||||||
|
middleware: "auth"
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const {bankAccounts} = storeToRefs(useDataStore())
|
||||||
|
const mode = ref("show")
|
||||||
|
|
||||||
|
const itemColumns = [
|
||||||
|
|
||||||
|
{
|
||||||
|
key: "name",
|
||||||
|
label: "Name",
|
||||||
|
sortable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "iban",
|
||||||
|
label: "IBAN",
|
||||||
|
sortable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "bankId",
|
||||||
|
label: "Bank",
|
||||||
|
sortable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "ownerName",
|
||||||
|
label: "Besitzer",
|
||||||
|
sortable: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
const selectItem = (item) => {
|
||||||
|
router.push(`/banking/statements/${item.id} `)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const searchString = ref('')
|
||||||
|
|
||||||
|
const filteredRows = computed(() => {
|
||||||
|
bankAccounts.value = bankAccounts.value.filter(account => account.used)
|
||||||
|
|
||||||
|
if(!searchString.value) {
|
||||||
|
return bankAccounts.value
|
||||||
|
}
|
||||||
|
|
||||||
|
return bankAccounts.value.filter(item => {
|
||||||
|
return Object.values(item).some((value) => {
|
||||||
|
return String(value).toLowerCase().includes(searchString.value.toLowerCase())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
142
spaces/pages/banking/statements/[[accountId]].vue
Normal file
142
spaces/pages/banking/statements/[[accountId]].vue
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
<script setup>
|
||||||
|
import * as dayjs from "dayjs";
|
||||||
|
|
||||||
|
definePageMeta({
|
||||||
|
middleware: "auth"
|
||||||
|
})
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const supabase = useSupabaseClient()
|
||||||
|
|
||||||
|
const {bankStatements,bankAccounts} = storeToRefs(useDataStore())
|
||||||
|
|
||||||
|
const searchString = ref("")
|
||||||
|
const showAssigned = ref(false)
|
||||||
|
const selectedAccount = ref(0)
|
||||||
|
|
||||||
|
const filteredRows = computed(() => {
|
||||||
|
let statements = bankStatements.value
|
||||||
|
|
||||||
|
if(!showAssigned.value) {
|
||||||
|
statements = statements.filter(statement => !(statement.customerInvoice || statement.vendorInvoice))
|
||||||
|
}
|
||||||
|
|
||||||
|
if(selectedAccount.value !== 0) {
|
||||||
|
statements = statements.filter(statement => statement.account === selectedAccount.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(searchString.value.length > 0) {
|
||||||
|
return statements.value.filter(item => {
|
||||||
|
return Object.values(item).some((value) => {
|
||||||
|
return String(value).toLowerCase().includes(searchString.value.toLowerCase())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return statements
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const showStatementSlideover = ref(false)
|
||||||
|
const selectedStatement = ref({})
|
||||||
|
|
||||||
|
const selectStatement = (statement) => {
|
||||||
|
selectedStatement.value = statement
|
||||||
|
showStatementSlideover.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const statementColumns = [
|
||||||
|
{
|
||||||
|
key:"amount",
|
||||||
|
label: "Betrag"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key:"date",
|
||||||
|
label: "Datum",
|
||||||
|
sortable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "credName",
|
||||||
|
label: "Empfänger"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "debName",
|
||||||
|
label: "Sender"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "text",
|
||||||
|
label: "Verwendungszweck"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<USlideover
|
||||||
|
v-model="showStatementSlideover"
|
||||||
|
>
|
||||||
|
<UCard class="flex flex-col flex-1" :ui="{ body: { base: 'flex-1' }, ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<DevOnly>
|
||||||
|
{{selectedStatement}}
|
||||||
|
</DevOnly>
|
||||||
|
</UCard>
|
||||||
|
|
||||||
|
</USlideover>
|
||||||
|
|
||||||
|
<InputGroup gap="2">
|
||||||
|
<UInput
|
||||||
|
v-model="searchString"
|
||||||
|
placeholder="Suche..."
|
||||||
|
/>
|
||||||
|
<USelectMenu
|
||||||
|
:options="bankAccounts.filter(account => account.used)"
|
||||||
|
v-model="selectedAccount"
|
||||||
|
option-attribute="iban"
|
||||||
|
value-attribute="id"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
{{bankAccounts.find(account => account.id === selectedAccount) ? bankAccounts.find(account => account.id === selectedAccount).iban : "Kontoauswählen"}}
|
||||||
|
</template>
|
||||||
|
</USelectMenu>
|
||||||
|
<UCheckbox
|
||||||
|
v-model="showAssigned"
|
||||||
|
label="Zugeordnete Anzeigen"
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
|
||||||
|
<UTable
|
||||||
|
:rows="bankStatements"
|
||||||
|
:columns="statementColumns"
|
||||||
|
@select="selectStatement"
|
||||||
|
>
|
||||||
|
<template #amount-data="{row}">
|
||||||
|
<span
|
||||||
|
v-if="row.amount >= 0"
|
||||||
|
class="text-primary-500"
|
||||||
|
>
|
||||||
|
{{row.amount.toFixed(2) + " €"}}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-if="row.amount < 0"
|
||||||
|
class="text-rose-600"
|
||||||
|
>
|
||||||
|
{{row.amount.toFixed(2) + " €"}}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template #date-data="{row}">
|
||||||
|
{{dayjs(row.date).format("DD.MM.YY")}}
|
||||||
|
</template>
|
||||||
|
</UTable>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
291
spaces/pages/contacts/[mode]/[[id]].vue
Normal file
291
spaces/pages/contacts/[mode]/[[id]].vue
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
<script setup>
|
||||||
|
definePageMeta({
|
||||||
|
middleware: "auth"
|
||||||
|
})
|
||||||
|
|
||||||
|
//
|
||||||
|
const supabase = useSupabaseClient()
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
const toast = useToast()
|
||||||
|
const id = ref(route.params.id ? route.params.id : null )
|
||||||
|
|
||||||
|
//Store
|
||||||
|
const {customers, vendors, contacts } = storeToRefs(useDataStore())
|
||||||
|
const {fetchContacts, getContactById} = useDataStore()
|
||||||
|
|
||||||
|
let currentContact = null
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//Working
|
||||||
|
const mode = ref(route.params.mode || "show")
|
||||||
|
const itemInfo = ref({
|
||||||
|
active: true
|
||||||
|
})
|
||||||
|
|
||||||
|
//Functions
|
||||||
|
const setupPage = () => {
|
||||||
|
if(mode.value === "show" || mode.value === "edit"){
|
||||||
|
currentContact = getContactById(Number(useRoute().params.id))
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mode.value === "edit") itemInfo.value = currentContact
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const createItem = async () => {
|
||||||
|
let fullName = itemInfo.value.firstName + ' ' + itemInfo.value.lastName
|
||||||
|
|
||||||
|
|
||||||
|
const {data,error} = await supabase
|
||||||
|
.from("contacts")
|
||||||
|
.insert([{...itemInfo.value, fullName}])
|
||||||
|
.select()
|
||||||
|
|
||||||
|
if(error) {
|
||||||
|
console.log(error)
|
||||||
|
} else {
|
||||||
|
mode.value = "show"
|
||||||
|
itemInfo.value = {
|
||||||
|
id: 0,
|
||||||
|
}
|
||||||
|
toast.add({title: "Kontakt erfolgreich erstellt"})
|
||||||
|
await fetchContacts()
|
||||||
|
router.push(`/contacts/show/${data[0].id}`)
|
||||||
|
setupPage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const editCustomer = async () => {
|
||||||
|
router.push(`/contacts/edit/${currentContact.id}`)
|
||||||
|
setupPage()
|
||||||
|
}
|
||||||
|
|
||||||
|
const cancelEditorCreate = () => {
|
||||||
|
mode.value = "show"
|
||||||
|
itemInfo.value = {
|
||||||
|
id: 0,
|
||||||
|
name: "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateCustomer = async () => {
|
||||||
|
const {error} = await supabase
|
||||||
|
.from("contacts")
|
||||||
|
.update(itemInfo.value)
|
||||||
|
.eq('id',itemInfo.value.id)
|
||||||
|
console.log(error)
|
||||||
|
mode.value = "show"
|
||||||
|
itemInfo.value = {
|
||||||
|
id: 0,
|
||||||
|
name: "",
|
||||||
|
}
|
||||||
|
toast.add({title: "Kontakt erfolgreich gespeichert"})
|
||||||
|
fetchContacts()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
setupPage()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<UCard v-if="currentContact && mode == 'show'" >
|
||||||
|
<template #header>
|
||||||
|
<UBadge
|
||||||
|
v-if="currentContact.active"
|
||||||
|
>
|
||||||
|
Kontakt aktiv
|
||||||
|
</UBadge>
|
||||||
|
<UBadge
|
||||||
|
v-else
|
||||||
|
color="red"
|
||||||
|
>
|
||||||
|
Kontakt inaktiv
|
||||||
|
</UBadge>
|
||||||
|
{{currentContact.fullName}}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<InputGroup class="mb-3">
|
||||||
|
<UButton
|
||||||
|
v-if="currentContact.customer"
|
||||||
|
:to="`/customers/show/${currentContact.customer}`"
|
||||||
|
>
|
||||||
|
Zum Kunden
|
||||||
|
</UButton>
|
||||||
|
<UButton
|
||||||
|
v-if="currentContact.vendor"
|
||||||
|
:to="`/vendors/show/${currentContact.vendor}`"
|
||||||
|
>
|
||||||
|
Zum Lieferanten
|
||||||
|
</UButton>
|
||||||
|
</InputGroup>
|
||||||
|
|
||||||
|
<span v-if="currentContact.customer">Kunde: {{customers.find(customer => customer.id === currentContact.customer) ? customers.find(customer => customer.id === currentContact.customer).name : "" }}</span><br>
|
||||||
|
<span v-if="currentContact.vendor">Lieferant: {{vendors.find(vendor => vendor.id === currentContact.vendor) ? vendors.find(vendor => vendor.id === currentContact.vendor).name : ""}}</span><br>
|
||||||
|
|
||||||
|
<span>E-Mail: {{currentContact.email}}</span><br>
|
||||||
|
<span>Mobil: {{currentContact.phoneMobile}}</span><br>
|
||||||
|
<span>Festnetz: {{currentContact.phoneHome}}</span><br>
|
||||||
|
<span>Rolle: {{currentContact.role}}</span>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<DevOnly>
|
||||||
|
<UDivider
|
||||||
|
class="my-3"
|
||||||
|
/>
|
||||||
|
{{currentContact}}
|
||||||
|
</DevOnly>
|
||||||
|
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<UButton
|
||||||
|
v-if="mode == 'show' && currentContact.id"
|
||||||
|
@click="editCustomer"
|
||||||
|
>
|
||||||
|
Bearbeiten
|
||||||
|
</UButton>
|
||||||
|
<UButton
|
||||||
|
color="red"
|
||||||
|
class="ml-2"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
Archivieren
|
||||||
|
</UButton>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</UCard>
|
||||||
|
<UCard v-else-if="mode == 'edit' || mode == 'create'" >
|
||||||
|
<template #header>
|
||||||
|
{{itemInfo.fullName}}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<UFormGroup
|
||||||
|
label="Anrede:"
|
||||||
|
>
|
||||||
|
<UInput
|
||||||
|
v-model="itemInfo.salutation"
|
||||||
|
/>
|
||||||
|
</UFormGroup>
|
||||||
|
<UFormGroup
|
||||||
|
label="Vorname:"
|
||||||
|
>
|
||||||
|
<UInput
|
||||||
|
v-model="itemInfo.firstName"
|
||||||
|
/>
|
||||||
|
</UFormGroup>
|
||||||
|
<UFormGroup
|
||||||
|
label="Nachname:"
|
||||||
|
>
|
||||||
|
<UInput
|
||||||
|
v-model="itemInfo.lastName"
|
||||||
|
/>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup
|
||||||
|
label="Kunde:"
|
||||||
|
>
|
||||||
|
<USelectMenu
|
||||||
|
v-model="itemInfo.customer"
|
||||||
|
option-attribute="name"
|
||||||
|
value-attribute="id"
|
||||||
|
:options="customers"
|
||||||
|
searchable
|
||||||
|
:search-attributes="['name']"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
{{customers.find(customer => customer.id === itemInfo.customer) ? customers.find(customer => customer.id === itemInfo.customer).name : "Kunde auswählen"}}
|
||||||
|
</template>
|
||||||
|
</USelectMenu>
|
||||||
|
</UFormGroup>
|
||||||
|
<UFormGroup
|
||||||
|
label="Lieferant:"
|
||||||
|
>
|
||||||
|
<USelectMenu
|
||||||
|
v-model="itemInfo.vendor"
|
||||||
|
option-attribute="name"
|
||||||
|
value-attribute="id"
|
||||||
|
:options="vendors"
|
||||||
|
searchable
|
||||||
|
:search-attributes="['name']"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
{{vendors.find(vendor => vendor.id === itemInfo.vendor) ? vendors.find(vendor => vendor.id === itemInfo.vendor).name : "Lieferant auswählen"}}
|
||||||
|
</template>
|
||||||
|
</USelectMenu>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup
|
||||||
|
label="Kontakt aktiv:"
|
||||||
|
>
|
||||||
|
<UCheckbox
|
||||||
|
v-model="itemInfo.active"
|
||||||
|
/>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup
|
||||||
|
label="E-Mail:"
|
||||||
|
>
|
||||||
|
<UInput
|
||||||
|
v-model="itemInfo.email"
|
||||||
|
/>
|
||||||
|
</UFormGroup>
|
||||||
|
<UFormGroup
|
||||||
|
label="Mobil:"
|
||||||
|
>
|
||||||
|
<UInput
|
||||||
|
v-model="itemInfo.phoneMobile"
|
||||||
|
/>
|
||||||
|
</UFormGroup>
|
||||||
|
<UFormGroup
|
||||||
|
label="Festnetz:"
|
||||||
|
>
|
||||||
|
<UInput
|
||||||
|
v-model="itemInfo.phoneHome"
|
||||||
|
/>
|
||||||
|
</UFormGroup>
|
||||||
|
<UFormGroup
|
||||||
|
label="Rolle:"
|
||||||
|
>
|
||||||
|
<UInput
|
||||||
|
v-model="itemInfo.role"
|
||||||
|
/>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<UButton
|
||||||
|
v-if="mode == 'edit'"
|
||||||
|
@click="updateCustomer"
|
||||||
|
>
|
||||||
|
Speichern
|
||||||
|
</UButton>
|
||||||
|
<UButton
|
||||||
|
v-else-if="mode == 'create'"
|
||||||
|
@click="createItem"
|
||||||
|
>
|
||||||
|
Erstellen
|
||||||
|
</UButton>
|
||||||
|
<UButton
|
||||||
|
@click="cancelEditorCreate"
|
||||||
|
color="red"
|
||||||
|
class="ml-2"
|
||||||
|
>
|
||||||
|
Abbrechen
|
||||||
|
</UButton>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</UCard>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
90
spaces/pages/contacts/index.vue
Normal file
90
spaces/pages/contacts/index.vue
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
<template>
|
||||||
|
<div id="main">
|
||||||
|
<!-- TODO: Kontakte erstellen und dem Kunden zuweisen -->
|
||||||
|
|
||||||
|
<InputGroup>
|
||||||
|
<UButton @click="router.push(`/contacts/create/`)">+ Kontakt</UButton>
|
||||||
|
|
||||||
|
<UInput
|
||||||
|
v-model="searchString"
|
||||||
|
placeholder="Suche..."
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<UTable
|
||||||
|
:rows="filteredRows"
|
||||||
|
:columns="itemColumns"
|
||||||
|
@select="selectItem"
|
||||||
|
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
|
||||||
|
>
|
||||||
|
<template #customer-data="{row}">
|
||||||
|
{{customers.find(customer => customer.id === row.customer) ? customers.find(customer => customer.id === row.customer).name : ''}}
|
||||||
|
</template>
|
||||||
|
<template #vendor-data="{row}">
|
||||||
|
{{vendors.find(vendor => vendor.id === row.vendor) ? vendors.find(vendor => vendor.id === row.vendor).name : ''}}
|
||||||
|
</template>
|
||||||
|
</UTable>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
|
||||||
|
definePageMeta({
|
||||||
|
middleware: "auth"
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const {contacts,customers,vendors} = storeToRefs(useDataStore())
|
||||||
|
const mode = ref("show")
|
||||||
|
|
||||||
|
const itemColumns = [
|
||||||
|
|
||||||
|
{
|
||||||
|
key: "fullName",
|
||||||
|
label: "Name",
|
||||||
|
sortable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "customer",
|
||||||
|
label: "Kunde",
|
||||||
|
sortable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "vendor",
|
||||||
|
label: "Lieferant",
|
||||||
|
sortable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "role",
|
||||||
|
label: "Rolle",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
const selectItem = (item) => {
|
||||||
|
router.push(`/contacts/show/${item.id} `)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const searchString = ref('')
|
||||||
|
|
||||||
|
const filteredRows = computed(() => {
|
||||||
|
if(!searchString.value) {
|
||||||
|
return contacts.value
|
||||||
|
}
|
||||||
|
|
||||||
|
return contacts.value.filter(item => {
|
||||||
|
return Object.values(item).some((value) => {
|
||||||
|
return String(value).toLowerCase().includes(searchString.value.toLowerCase())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -119,7 +119,9 @@ setupPage()
|
|||||||
Beschreibung:<br>
|
Beschreibung:<br>
|
||||||
{{currentContract.description}}<br>
|
{{currentContract.description}}<br>
|
||||||
|
|
||||||
|
<DevOnly>
|
||||||
|
{{currentContract}}
|
||||||
|
</DevOnly>
|
||||||
|
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ const toast = useToast()
|
|||||||
const id = ref(route.params.id ? route.params.id : null )
|
const id = ref(route.params.id ? route.params.id : null )
|
||||||
|
|
||||||
//Store
|
//Store
|
||||||
const {customers } = storeToRefs(useDataStore())
|
const {customers, contacts } = storeToRefs(useDataStore())
|
||||||
const {fetchCustomers, getCustomerById} = useDataStore()
|
const {fetchCustomers, getCustomerById, getContactsByCustomerId} = useDataStore()
|
||||||
|
|
||||||
let currentCustomer = null
|
let currentCustomer = null
|
||||||
|
|
||||||
@@ -130,6 +130,24 @@ setupPage()
|
|||||||
Notizen:<br>
|
Notizen:<br>
|
||||||
{{currentCustomer.notes}}<br>
|
{{currentCustomer.notes}}<br>
|
||||||
|
|
||||||
|
<UDivider
|
||||||
|
class="my-2"
|
||||||
|
/>
|
||||||
|
|
||||||
|
Kontakte: <br>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Anrede</th>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Rolle</th>
|
||||||
|
</tr>
|
||||||
|
<tr v-for="contact in getContactsByCustomerId(currentCustomer.id)">
|
||||||
|
<td>{{contact.salutation}}</td>
|
||||||
|
<td>{{contact.fullName}}</td>
|
||||||
|
<td>{{contact.role}}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
<!-- Kontakte:<br>
|
<!-- Kontakte:<br>
|
||||||
<!– <ul>
|
<!– <ul>
|
||||||
<li v-for="contact in currentCustomer.contacts.data">{{contact.lastName}}, {{contact.firstName}}</li>
|
<li v-for="contact in currentCustomer.contacts.data">{{contact.lastName}}, {{contact.firstName}}</li>
|
||||||
@@ -279,5 +297,7 @@ setupPage()
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
td {
|
||||||
|
padding: 0.2em;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -174,7 +174,11 @@ setupPage()
|
|||||||
value-attribute="id"
|
value-attribute="id"
|
||||||
searchable
|
searchable
|
||||||
:search-attributes="['name']"
|
:search-attributes="['name']"
|
||||||
/>
|
>
|
||||||
|
<template #label>
|
||||||
|
{{customers.find(customer => customer.id === itemInfo.customer) ? customers.find(customer => customer.id === itemInfo.customer).name : "Kunde auswählen"}}
|
||||||
|
</template>
|
||||||
|
</USelectMenu>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
16
spaces/pages/settings/users.vue
Normal file
16
spaces/pages/settings/users.vue
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
definePageMeta({
|
||||||
|
middleware: "auth"
|
||||||
|
})
|
||||||
|
|
||||||
|
const {profiles} = storeToRefs(useDataStore())
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
{{profiles}}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -12,7 +12,7 @@ const supabase = useSupabaseClient()
|
|||||||
const user = useSupabaseUser()
|
const user = useSupabaseUser()
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
|
|
||||||
const {times, projects, profiles} = storeToRefs(useDataStore())
|
const {times, projects, profiles, jobs} = storeToRefs(useDataStore())
|
||||||
const {fetchTimes, getTimeTypes} = useDataStore()
|
const {fetchTimes, getTimeTypes} = useDataStore()
|
||||||
const timeTypes = getTimeTypes
|
const timeTypes = getTimeTypes
|
||||||
const timeInfo = ref({
|
const timeInfo = ref({
|
||||||
@@ -71,6 +71,10 @@ const columns = [
|
|||||||
key: "projectId",
|
key: "projectId",
|
||||||
label: "Projekt"
|
label: "Projekt"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "job",
|
||||||
|
label: "Job"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "notes",
|
key: "notes",
|
||||||
label: "Notizen"
|
label: "Notizen"
|
||||||
@@ -218,6 +222,21 @@ const selectStartedTime = () => {
|
|||||||
</USelectMenu>
|
</USelectMenu>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup
|
||||||
|
label="Job:"
|
||||||
|
>
|
||||||
|
<USelectMenu
|
||||||
|
:options="jobs"
|
||||||
|
option-attribute="title"
|
||||||
|
value-attribute="id"
|
||||||
|
v-model="runningTimeInfo.job"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
{{ jobs.find(job => job.id === runningTimeInfo.job) ? jobs.find(job => job.id === runningTimeInfo.job).title : "Job auswählen" }}
|
||||||
|
</template>
|
||||||
|
</USelectMenu>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
<UFormGroup
|
<UFormGroup
|
||||||
label="Kategorie:"
|
label="Kategorie:"
|
||||||
>
|
>
|
||||||
@@ -307,6 +326,20 @@ const selectStartedTime = () => {
|
|||||||
</template>
|
</template>
|
||||||
</USelectMenu>
|
</USelectMenu>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
<UFormGroup
|
||||||
|
label="Job:"
|
||||||
|
>
|
||||||
|
<USelectMenu
|
||||||
|
:options="jobs"
|
||||||
|
option-attribute="title"
|
||||||
|
value-attribute="id"
|
||||||
|
v-model="createTimeInfo.job"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
{{ jobs.find(job => job.id === runningTimeInfo.job) ? jobs.find(job => job.id === runningTimeInfo.job).title : "Job auswählen" }}
|
||||||
|
</template>
|
||||||
|
</USelectMenu>
|
||||||
|
</UFormGroup>
|
||||||
<UFormGroup
|
<UFormGroup
|
||||||
label="Typ:"
|
label="Typ:"
|
||||||
>
|
>
|
||||||
@@ -361,6 +394,9 @@ const selectStartedTime = () => {
|
|||||||
<template #projectId-data="{row}">
|
<template #projectId-data="{row}">
|
||||||
{{projects.find(project => project.id === row.projectId) ? projects.find(project => project.id === row.projectId).name : ""}}
|
{{projects.find(project => project.id === row.projectId) ? projects.find(project => project.id === row.projectId).name : ""}}
|
||||||
</template>
|
</template>
|
||||||
|
<template #job-data="{row}">
|
||||||
|
{{jobs.find(job => job.id === row.job) ? jobs.find(job => job.id === row.job).title : ""}}
|
||||||
|
</template>
|
||||||
</UTable>
|
</UTable>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
223
spaces/pages/vehicles/[mode]/[[id]].vue
Normal file
223
spaces/pages/vehicles/[mode]/[[id]].vue
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
<script setup>
|
||||||
|
definePageMeta({
|
||||||
|
middleware: "auth"
|
||||||
|
})
|
||||||
|
|
||||||
|
//
|
||||||
|
const supabase = useSupabaseClient()
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
const toast = useToast()
|
||||||
|
const id = ref(route.params.id ? route.params.id : null )
|
||||||
|
|
||||||
|
//Store
|
||||||
|
const {vehicles, profiles } = storeToRefs(useDataStore())
|
||||||
|
const {fetchVehicles, getVehicleById} = useDataStore()
|
||||||
|
|
||||||
|
let currentItem = null
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//Working
|
||||||
|
const mode = ref(route.params.mode || "show")
|
||||||
|
const itemInfo = ref({
|
||||||
|
id: 0,
|
||||||
|
name: "",
|
||||||
|
licensePlate: "",
|
||||||
|
type: "",
|
||||||
|
driver: ""
|
||||||
|
})
|
||||||
|
|
||||||
|
//Functions
|
||||||
|
const setupPage = () => {
|
||||||
|
if(mode.value === "show" || mode.value === "edit"){
|
||||||
|
currentItem = getVehicleById(Number(useRoute().params.id))
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mode.value === "edit") itemInfo.value = currentItem
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const createItem = async () => {
|
||||||
|
const {data,error} = await supabase
|
||||||
|
.from("vehicles")
|
||||||
|
.insert([itemInfo.value])
|
||||||
|
.select()
|
||||||
|
|
||||||
|
if(error) {
|
||||||
|
console.log(error)
|
||||||
|
} else {
|
||||||
|
mode.value = "show"
|
||||||
|
itemInfo.value = {
|
||||||
|
id: 0,
|
||||||
|
name: ""
|
||||||
|
}
|
||||||
|
toast.add({title: "Fahrzeug erfolgreich erstellt"})
|
||||||
|
await fetchVehicles()
|
||||||
|
router.push(`/vehicles/show/${data[0].id}`)
|
||||||
|
setupPage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const editCustomer = async () => {
|
||||||
|
router.push(`/vehicles/edit/${currentItem.id}`)
|
||||||
|
setupPage()
|
||||||
|
}
|
||||||
|
|
||||||
|
const cancelEditorCreate = () => {
|
||||||
|
mode.value = "show"
|
||||||
|
itemInfo.value = {
|
||||||
|
id: 0,
|
||||||
|
name: "",
|
||||||
|
licensePlate: "",
|
||||||
|
type: ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateCustomer = async () => {
|
||||||
|
const {error} = await supabase
|
||||||
|
.from("vehicles")
|
||||||
|
.update(itemInfo.value)
|
||||||
|
.eq('id',itemInfo.value.id)
|
||||||
|
if(error) {
|
||||||
|
console.log(error)
|
||||||
|
} else {
|
||||||
|
mode.value = "show"
|
||||||
|
itemInfo.value = {
|
||||||
|
id: 0,
|
||||||
|
name: "",
|
||||||
|
licensePlate: "",
|
||||||
|
type: ""
|
||||||
|
}
|
||||||
|
toast.add({title: "Fahrzeug erfolgreich gespeichert"})
|
||||||
|
fetchVehicles()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
setupPage()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<UCard v-if="currentItem && mode == 'show'" >
|
||||||
|
<template #header>
|
||||||
|
<UBadge
|
||||||
|
v-if="currentItem.active"
|
||||||
|
>
|
||||||
|
Fahrzeug aktiv
|
||||||
|
</UBadge>
|
||||||
|
<UBadge
|
||||||
|
v-else
|
||||||
|
color="red"
|
||||||
|
>
|
||||||
|
Fahrzeug gesperrt
|
||||||
|
</UBadge>
|
||||||
|
{{currentItem.licensePlate}}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
Typ: {{currentItem.type}} <br>
|
||||||
|
Fahrer: {{profiles.find(profile => profile.id === currentItem.driver) ? profiles.find(profile => profile.id === currentItem.driver).fullName : 'Kein Fahrer gewählt'}} <br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<UButton
|
||||||
|
v-if="mode == 'show' && currentItem.id"
|
||||||
|
@click="editCustomer"
|
||||||
|
>
|
||||||
|
Bearbeiten
|
||||||
|
</UButton>
|
||||||
|
<UButton
|
||||||
|
color="red"
|
||||||
|
class="ml-2"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
Archivieren
|
||||||
|
</UButton>
|
||||||
|
<!-- TODO: Fahrzeug archivieren -->
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</UCard>
|
||||||
|
<UCard v-else-if="mode == 'edit' || mode == 'create'" >
|
||||||
|
<template #header>
|
||||||
|
{{itemInfo.licensePlate}}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<UFormGroup
|
||||||
|
label="Kennzeichen:"
|
||||||
|
>
|
||||||
|
<UInput
|
||||||
|
v-model="itemInfo.licensePlate"
|
||||||
|
/>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<UFormGroup
|
||||||
|
label="Fahrzeug aktiv:"
|
||||||
|
>
|
||||||
|
<UCheckbox
|
||||||
|
v-model="itemInfo.active"
|
||||||
|
/>
|
||||||
|
</UFormGroup>
|
||||||
|
<UFormGroup
|
||||||
|
label="Typ:"
|
||||||
|
>
|
||||||
|
<UTextarea
|
||||||
|
v-model="itemInfo.type"
|
||||||
|
/>
|
||||||
|
</UFormGroup>
|
||||||
|
<UFormGroup
|
||||||
|
label="Fahrer:"
|
||||||
|
>
|
||||||
|
<USelectMenu
|
||||||
|
v-model="itemInfo.driver"
|
||||||
|
:options="profiles"
|
||||||
|
option-attribute="fullName"
|
||||||
|
value-attribute="id"
|
||||||
|
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
{{profiles.find(profile => profile.id === itemInfo.driver) ? profiles.find(profile => profile.id === itemInfo.driver).fullName : 'Kein Fahrer ausgewählt'}}
|
||||||
|
</template>
|
||||||
|
</USelectMenu>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<UButton
|
||||||
|
v-if="mode == 'edit'"
|
||||||
|
@click="updateCustomer"
|
||||||
|
>
|
||||||
|
Speichern
|
||||||
|
</UButton>
|
||||||
|
<UButton
|
||||||
|
v-else-if="mode == 'create'"
|
||||||
|
@click="createItem"
|
||||||
|
>
|
||||||
|
Erstellen
|
||||||
|
</UButton>
|
||||||
|
<UButton
|
||||||
|
@click="cancelEditorCreate"
|
||||||
|
color="red"
|
||||||
|
class="ml-2"
|
||||||
|
>
|
||||||
|
Abbrechen
|
||||||
|
</UButton>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</UCard>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
74
spaces/pages/vehicles/index.vue
Normal file
74
spaces/pages/vehicles/index.vue
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<template>
|
||||||
|
<div id="main">
|
||||||
|
|
||||||
|
<div class="flex items-center gap-1">
|
||||||
|
<UButton @click="router.push(`/vehicles/create/`)">+ Fahrzeug</UButton>
|
||||||
|
|
||||||
|
<UInput
|
||||||
|
v-model="searchString"
|
||||||
|
placeholder="Suche..."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<UTable
|
||||||
|
:rows="filteredRows"
|
||||||
|
:columns="itemColumns"
|
||||||
|
@select="selectItem"
|
||||||
|
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
|
||||||
|
>
|
||||||
|
|
||||||
|
</UTable>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
|
||||||
|
definePageMeta({
|
||||||
|
middleware: "auth"
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const {vehicles } = storeToRefs(useDataStore())
|
||||||
|
const mode = ref("show")
|
||||||
|
|
||||||
|
const itemColumns = [
|
||||||
|
{
|
||||||
|
key: 'licensePlate',
|
||||||
|
label: "Kennzeichen:",
|
||||||
|
sortable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "type",
|
||||||
|
label: "Typ:",
|
||||||
|
sortable: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
const selectItem = (item) => {
|
||||||
|
router.push(`/vehicles/show/${item.id} `)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const searchString = ref('')
|
||||||
|
|
||||||
|
const filteredRows = computed(() => {
|
||||||
|
if(!searchString.value) {
|
||||||
|
return vehicles.value
|
||||||
|
}
|
||||||
|
|
||||||
|
return vehicles.value.filter(item => {
|
||||||
|
return Object.values(item).some((value) => {
|
||||||
|
return String(value).toLowerCase().includes(searchString.value.toLowerCase())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -9,146 +9,166 @@
|
|||||||
<div
|
<div
|
||||||
class="previewDoc"
|
class="previewDoc"
|
||||||
>
|
>
|
||||||
<!-- <embed
|
<embed
|
||||||
:src="fileurl + '#toolbar=0&navpanes=0&scrollbar=0&statusbar=0&messages=0&scrollbar=0'"
|
v-if="currentDocument"
|
||||||
width="40vw"
|
:src="currentDocument.url + '#toolbar=0&navpanes=0&scrollbar=0&statusbar=0&messages=0&scrollbar=0'"
|
||||||
height="50vh"
|
|
||||||
>-->
|
>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="inputData"
|
class="inputData"
|
||||||
>
|
>
|
||||||
<UFormGroup label="Lieferant:" required>
|
<UFormGroup label="Lieferant:" required>
|
||||||
<USelectMenu
|
<USelectMenu
|
||||||
v-model="invoice.vendor"
|
v-model="itemInfo.vendor"
|
||||||
:options="vendors"
|
:options="vendors"
|
||||||
option-attribute="name"
|
option-attribute="name"
|
||||||
value-attribute="id"
|
value-attribute="id"
|
||||||
searchable
|
searchable
|
||||||
:search-attributes="['name','vendorNumber']"
|
:search-attributes="['name','vendorNumber']"
|
||||||
>
|
>
|
||||||
|
<template #label>
|
||||||
|
{{vendors.find(vendor => vendor.id === itemInfo.vendor) ? vendors.find(vendor => vendor.id === itemInfo.vendor).name : 'Lieferant auswählen'}}
|
||||||
|
</template>
|
||||||
</USelectMenu>
|
</USelectMenu>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
<UFormGroup label="Rechnungsreferenz:" required>
|
<UFormGroup
|
||||||
|
class="mt-3"
|
||||||
|
label="Rechnungsreferenz:"
|
||||||
|
required
|
||||||
|
>
|
||||||
<UInput
|
<UInput
|
||||||
v-model="invoice.reference"
|
v-model="itemInfo.reference"
|
||||||
/>
|
/>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
<UFormGroup label="Rechnungsdatum:" required>
|
<InputGroup class="mt-3" gap="2">
|
||||||
<UPopover :popper="{ placement: 'bottom-start' }">
|
<UFormGroup label="Rechnungsdatum:" required>
|
||||||
<UButton icon="i-heroicons-calendar-days-20-solid" :label="labelDate" />
|
<UPopover :popper="{ placement: 'bottom-start' }">
|
||||||
|
<UButton icon="i-heroicons-calendar-days-20-solid" :label="itemInfo.date ? dayjs(itemInfo.date).format('DD.MM.YYYY') : 'Datum auswählen'" />
|
||||||
|
|
||||||
<template #panel="{ close }">
|
<template #panel="{ close }">
|
||||||
<LazyDatePicker v-model="invoice.date" @close="close" />
|
<LazyDatePicker v-model="itemInfo.date" @close="close" />
|
||||||
</template>
|
</template>
|
||||||
</UPopover>
|
</UPopover>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Fälligkeitsdatum:" required>
|
||||||
|
<UPopover :popper="{ placement: 'bottom-start' }">
|
||||||
|
<UButton icon="i-heroicons-calendar-days-20-solid" :label="itemInfo.dueDate ? dayjs(itemInfo.dueDate).format('DD.MM.YYYY') : 'Datum auswählen'" />
|
||||||
|
|
||||||
|
<template #panel="{ close }">
|
||||||
|
<LazyDatePicker v-model="itemInfo.dueDate" @close="close" />
|
||||||
|
</template>
|
||||||
|
</UPopover>
|
||||||
|
</UFormGroup>
|
||||||
|
</InputGroup>
|
||||||
|
|
||||||
<UFormGroup label="Fälligkeitsdatum:" required>
|
|
||||||
<UPopover :popper="{ placement: 'bottom-start' }">
|
|
||||||
<UButton icon="i-heroicons-calendar-days-20-solid" :label="labelDueDate" />
|
|
||||||
|
|
||||||
<template #panel="{ close }">
|
|
||||||
<LazyDatePicker v-model="invoice.dueDate" @close="close" />
|
|
||||||
</template>
|
|
||||||
</UPopover>
|
|
||||||
</UFormGroup>
|
|
||||||
|
|
||||||
<UFormGroup label="Beschreibung:" required>
|
<UFormGroup label="Beschreibung:" required>
|
||||||
<UTextarea
|
<UTextarea
|
||||||
v-model="invoice.description"
|
v-model="itemInfo.description"
|
||||||
/>
|
/>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
<!-- <UButton @click="vendorInvoiceData.lineItems.push({})">+ Reihe</UButton>-->
|
<UFormGroup label="Betrag:" required>
|
||||||
|
<UInput
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
v-model="itemInfo.amount"
|
||||||
|
>
|
||||||
|
<template #trailing>
|
||||||
|
<span class="text-gray-500 dark:text-gray-400 text-xs">EUR</span>
|
||||||
|
</template>
|
||||||
|
</UInput>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<InputGroup class="mt-3">
|
||||||
|
<UButton
|
||||||
|
@click="updateItem"
|
||||||
|
>
|
||||||
|
Speichern
|
||||||
|
</UButton>
|
||||||
|
</InputGroup>
|
||||||
|
|
||||||
|
<DevOnly>
|
||||||
|
{{itemInfo}}<br>
|
||||||
|
{{currentVendorInvoice}}<br>
|
||||||
|
{{currentDocument}}
|
||||||
|
</DevOnly>
|
||||||
|
|
||||||
|
|
||||||
<!-- <div v-for="lineItem in vendorInvoiceData.lineItems" class="lineItemRow">
|
|
||||||
<UFormGroup label="Text:" required>
|
|
||||||
<UInput v-model="lineItem.text"/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup label="Produkt:" required>
|
|
||||||
<UInput v-model="lineItem.productId"/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup label="Projekt:" required>
|
|
||||||
<UInput v-model="lineItem.projectId"/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup label="Anzahl:" required>
|
|
||||||
<UInput v-model="lineItem.quantity"/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup label="Einheit:" required>
|
|
||||||
<UInput v-model="lineItem.unit"/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup label="Einzelpreis:" required>
|
|
||||||
<UInput v-model="lineItem.unitPriceNet"/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup label="USt:" required>
|
|
||||||
<UInput v-model="lineItem.vat"/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup label="Rabatt:" required>
|
|
||||||
<UInput v-model="lineItem.discount"/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup label="Buchungskonto:" required>
|
|
||||||
<UInput v-model="lineItem.skrAccountId"/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup label="Positionspreis:" required>
|
|
||||||
<UInput disabled/>
|
|
||||||
</UFormGroup>
|
|
||||||
</div>-->
|
|
||||||
{{vendorInvoiceData}}<br>
|
|
||||||
{{currentVendorInvoice}}<br>
|
|
||||||
{{getDocumentById(78)}}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import InputGroup from "~/components/InputGroup.vue";
|
||||||
|
import * as dayjs from "dayjs";
|
||||||
|
|
||||||
const supabase = useSupabaseClient()
|
const supabase = useSupabaseClient()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const {vendors} = storeToRefs(useDataStore())
|
const {vendors} = storeToRefs(useDataStore())
|
||||||
const {getVendorInvoiceById, getDocumentById} = useDataStore()
|
const {getVendorInvoiceById, getDocumentById, fetchVendorInvoices} = useDataStore()
|
||||||
|
|
||||||
|
let currentVendorInvoice = null
|
||||||
|
let currentDocument = ref(null)
|
||||||
|
//Working
|
||||||
|
const mode = ref(route.params.mode || "show")
|
||||||
|
|
||||||
|
|
||||||
const invoice = ref({
|
//Functions
|
||||||
vendor: 0,
|
const setupPage = async () => {
|
||||||
reference: "",
|
if(mode.value === "show" || mode.value === "edit"){
|
||||||
date: new Date(),
|
currentVendorInvoice = await getVendorInvoiceById(Number(useRoute().params.id))
|
||||||
dueDate: new Date(),
|
currentDocument.value = await getDocumentById(currentVendorInvoice.document)
|
||||||
paymentType: "",
|
}
|
||||||
description: "",
|
|
||||||
|
if(mode.value === "edit") itemInfo.value = currentVendorInvoice
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const itemInfo = ref({
|
||||||
|
vendor: 0,
|
||||||
|
reference: "",
|
||||||
|
date: null,
|
||||||
|
dueDate: null,
|
||||||
|
paymentType: "",
|
||||||
|
description: "",
|
||||||
state: "Entwurf"
|
state: "Entwurf"
|
||||||
})
|
})
|
||||||
|
|
||||||
const labelDate = computed(() => invoice.value.date.toLocaleDateString('de-de', { weekday: 'long', year: 'numeric', month: 'short', day: 'numeric' }))
|
const updateItem = async () => {
|
||||||
const labelDueDate = computed(() => invoice.value.dueDate.toLocaleDateString('de-de', { weekday: 'long', year: 'numeric', month: 'short', day: 'numeric' }))
|
const {error} = await supabase
|
||||||
|
.from("vendorInvoices")
|
||||||
|
.update(itemInfo.value)
|
||||||
|
.eq('id',itemInfo.value.id)
|
||||||
const currentVendorInvoice = ref(getVendorInvoiceById(Number(route.params.id)))
|
if(error) {
|
||||||
console.log(currentVendorInvoice)
|
console.log(error)
|
||||||
const currentDocument = getDocumentById(Number(currentVendorInvoice.document))
|
} else {
|
||||||
console.log(currentDocument)
|
mode.value = "show"
|
||||||
//console.log(document)
|
/*itemInfo.value = {
|
||||||
|
id: 0,
|
||||||
//let fileurl = (await supabase.storage.from('documents').createSignedUrl(document.path,60*60)).data.signedUrl
|
}*/
|
||||||
|
toast.add({title: "Eingangsrechnung erfolgreich gespeichert"})
|
||||||
|
fetchVendorInvoices()
|
||||||
let vendorInvoiceData = ref({
|
}
|
||||||
reference: "",
|
}
|
||||||
date: "",
|
|
||||||
vendorId: 0,
|
|
||||||
lineItems: []
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
setupPage()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -158,7 +178,7 @@ let vendorInvoiceData = ref({
|
|||||||
}
|
}
|
||||||
|
|
||||||
.previewDoc {
|
.previewDoc {
|
||||||
width: 50vw;
|
min-width: 50vw;
|
||||||
min-height: 80vh;
|
min-height: 80vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,7 +188,7 @@ let vendorInvoiceData = ref({
|
|||||||
}
|
}
|
||||||
|
|
||||||
.inputData {
|
.inputData {
|
||||||
|
max-width: 40vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,34 @@
|
|||||||
:columns="itemColumns"
|
:columns="itemColumns"
|
||||||
@select="selectItem"
|
@select="selectItem"
|
||||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
|
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
|
||||||
/>
|
>
|
||||||
|
<template #state-data="{row}">
|
||||||
|
<span
|
||||||
|
v-if="row.state === 'Entwurf'"
|
||||||
|
class="text-cyan-500"
|
||||||
|
>
|
||||||
|
{{row.state}}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-if="row.state === 'Bezahlt'"
|
||||||
|
class="text-primary-500"
|
||||||
|
>
|
||||||
|
{{row.state}}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template #vendor-data="{row}">
|
||||||
|
{{vendors.find(vendor => vendor.id === row.vendor) ? vendors.find(vendor => vendor.id === row.vendor).name : ''}}
|
||||||
|
</template>
|
||||||
|
<template #date-data="{row}">
|
||||||
|
{{row.date ? dayjs(row.date).format("DD.MM.YY") : ''}}
|
||||||
|
</template>
|
||||||
|
<template #dueDate-data="{row}">
|
||||||
|
{{row.dueDate ? dayjs(row.dueDate).format("DD.MM.YY") : ''}}
|
||||||
|
</template>
|
||||||
|
<template #amount-data="{row}">
|
||||||
|
{{row.amount ? row.amount.toFixed(2) + ' €' : ''}}
|
||||||
|
</template>
|
||||||
|
</UTable>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -25,6 +52,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import * as dayjs from "dayjs";
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
middleware: "auth"
|
middleware: "auth"
|
||||||
@@ -32,15 +60,10 @@ definePageMeta({
|
|||||||
|
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const {vendorInvoices } = storeToRefs(useDataStore())
|
const {vendorInvoices, vendors} = storeToRefs(useDataStore())
|
||||||
const mode = ref("show")
|
const mode = ref("show")
|
||||||
|
|
||||||
const itemColumns = [
|
const itemColumns = [
|
||||||
{
|
|
||||||
key: 'id',
|
|
||||||
label: "Id",
|
|
||||||
sortable: true
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
key: 'state',
|
key: 'state',
|
||||||
label: "Status.",
|
label: "Status.",
|
||||||
@@ -48,7 +71,7 @@ const itemColumns = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'vendor',
|
key: 'vendor',
|
||||||
label: "Lieferantennr.",
|
label: "Lieferant",
|
||||||
sortable: true
|
sortable: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -61,12 +84,22 @@ const itemColumns = [
|
|||||||
label: "Datum",
|
label: "Datum",
|
||||||
sortable: true
|
sortable: true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "dueDate",
|
||||||
|
label: "Fällig:",
|
||||||
|
sortable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "amount",
|
||||||
|
label: "Betrag",
|
||||||
|
sortable: true
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
const selectItem = (item) => {
|
const selectItem = (item) => {
|
||||||
console.log(item)
|
console.log(item)
|
||||||
router.push(`/vendorinvoices/show/${item.id} `)
|
router.push(`/vendorinvoices/edit/${item.id} `)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -38,10 +38,13 @@ export const useDataStore = defineStore('data', {
|
|||||||
contacts: [] as any[],
|
contacts: [] as any[],
|
||||||
vehicles: [] as any[],
|
vehicles: [] as any[],
|
||||||
vendors: [] as any[],
|
vendors: [] as any[],
|
||||||
vendorInvoices: [] as any[]
|
vendorInvoices: [] as any[],
|
||||||
|
bankAccounts: [] as any[],
|
||||||
|
bankStatements: [] as any[]
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
async fetchData() {
|
async fetchData() {
|
||||||
|
this.fetchDocuments()
|
||||||
await this.fetchOwnTenant()
|
await this.fetchOwnTenant()
|
||||||
await this.fetchProfiles()
|
await this.fetchProfiles()
|
||||||
await this.fetchEvents()
|
await this.fetchEvents()
|
||||||
@@ -54,6 +57,7 @@ export const useDataStore = defineStore('data', {
|
|||||||
|
|
||||||
await this.fetchCustomers()
|
await this.fetchCustomers()
|
||||||
await this.fetchContracts()
|
await this.fetchContracts()
|
||||||
|
await this.fetchContacts()
|
||||||
await this.fetchForms()
|
await this.fetchForms()
|
||||||
await this.fetchFormSubmits()
|
await this.fetchFormSubmits()
|
||||||
await this.fetchProducts()
|
await this.fetchProducts()
|
||||||
@@ -64,7 +68,36 @@ export const useDataStore = defineStore('data', {
|
|||||||
await this.fetchVehicles()
|
await this.fetchVehicles()
|
||||||
await this.fetchVendors()
|
await this.fetchVendors()
|
||||||
await this.fetchVendorInvoices()
|
await this.fetchVendorInvoices()
|
||||||
|
await this.fetchBankAccounts()
|
||||||
|
await this.fetchBankStatements()
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
async clearStore() {
|
||||||
|
console.log("Clear")
|
||||||
|
this.loaded = false
|
||||||
|
this.ownTenant = {}
|
||||||
|
this.profiles= []
|
||||||
|
this.events= []
|
||||||
|
this.customers= []
|
||||||
|
this.tasks= []
|
||||||
|
this.projects= []
|
||||||
|
this.documents= []
|
||||||
|
this.spaces= []
|
||||||
|
this.units= []
|
||||||
|
this.times= []
|
||||||
|
this.products= []
|
||||||
|
this.movements= []
|
||||||
|
this.forms= []
|
||||||
|
this.contracts= []
|
||||||
|
this.jobs= []
|
||||||
|
this.formSubmits= []
|
||||||
|
this.contacts= []
|
||||||
|
this.vehicles= []
|
||||||
|
this.vendors= []
|
||||||
|
this.vendorInvoices= []
|
||||||
|
this.bankAccounts= []
|
||||||
|
this.bankStatements= []
|
||||||
},
|
},
|
||||||
async fetchOwnTenant() {
|
async fetchOwnTenant() {
|
||||||
//TODO: Tenant ID Dynamisch machen
|
//TODO: Tenant ID Dynamisch machen
|
||||||
@@ -75,6 +108,14 @@ export const useDataStore = defineStore('data', {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.profiles = (await supabase.from("profiles").select()).data
|
this.profiles = (await supabase.from("profiles").select()).data
|
||||||
},
|
},
|
||||||
|
async fetchBankAccounts() {
|
||||||
|
// @ts-ignore
|
||||||
|
this.bankAccounts = (await supabase.from("bankAccounts").select()).data
|
||||||
|
},
|
||||||
|
async fetchBankStatements() {
|
||||||
|
// @ts-ignore
|
||||||
|
this.bankStatements = (await supabase.from("bankStatements").select()).data
|
||||||
|
},
|
||||||
async fetchEvents() {
|
async fetchEvents() {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.events = (await supabase.from("events").select()).data
|
this.events = (await supabase.from("events").select()).data
|
||||||
@@ -83,6 +124,10 @@ export const useDataStore = defineStore('data', {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.contracts = (await supabase.from("contracts").select()).data
|
this.contracts = (await supabase.from("contracts").select()).data
|
||||||
},
|
},
|
||||||
|
async fetchContacts() {
|
||||||
|
// @ts-ignore
|
||||||
|
this.contacts = (await supabase.from("contacts").select()).data
|
||||||
|
},
|
||||||
async fetchCustomers() {
|
async fetchCustomers() {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.customers = (await supabase.from("customers").select().order('customerNumber', {ascending: true})).data
|
this.customers = (await supabase.from("customers").select().order('customerNumber', {ascending: true})).data
|
||||||
@@ -167,6 +212,8 @@ export const useDataStore = defineStore('data', {
|
|||||||
getVendorById: (state) => (itemId:number) => state.vendors.find(item => item.id === itemId),
|
getVendorById: (state) => (itemId:number) => state.vendors.find(item => item.id === itemId),
|
||||||
getVendorInvoiceById: (state) => (itemId:number) => state.vendorInvoices.find(item => item.id === itemId),
|
getVendorInvoiceById: (state) => (itemId:number) => state.vendorInvoices.find(item => item.id === itemId),
|
||||||
getContractById: (state) => (itemId:number) => state.contracts.find(item => item.id === itemId),
|
getContractById: (state) => (itemId:number) => state.contracts.find(item => item.id === itemId),
|
||||||
|
getContactById: (state) => (itemId:number) => state.contacts.find(item => item.id === itemId),
|
||||||
|
getVehicleById: (state) => (itemId:number) => state.vehicles.find(item => item.id === itemId),
|
||||||
getDocumentById: (state) => (itemId:number) => state.documents.find(item => item.id === itemId),
|
getDocumentById: (state) => (itemId:number) => state.documents.find(item => item.id === itemId),
|
||||||
getSpaceById: (state) => (itemId:number) => state.spaces.find(item => item.id === itemId),
|
getSpaceById: (state) => (itemId:number) => state.spaces.find(item => item.id === itemId),
|
||||||
getProjectById: (state) => (projectId:number) => {
|
getProjectById: (state) => (projectId:number) => {
|
||||||
@@ -184,6 +231,7 @@ export const useDataStore = defineStore('data', {
|
|||||||
},
|
},
|
||||||
getCustomerById: (state) => (customerId:number) => state.customers.find(customer => customer.id === customerId),
|
getCustomerById: (state) => (customerId:number) => state.customers.find(customer => customer.id === customerId),
|
||||||
getJobById: (state) => (jobId:number) => state.jobs.find(job => job.id === jobId),
|
getJobById: (state) => (jobId:number) => state.jobs.find(job => job.id === jobId),
|
||||||
|
getContactsByCustomerId: (state) => (customerId) => state.contacts.filter(contact => contact.customer === customerId),
|
||||||
getTimesByProjectId: (state) => (projectId:number) => {
|
getTimesByProjectId: (state) => (projectId:number) => {
|
||||||
let times = state.times.filter(time => time.projectId === projectId)
|
let times = state.times.filter(time => time.projectId === projectId)
|
||||||
console.log(times.length)
|
console.log(times.length)
|
||||||
|
|||||||
168
test/bankImport/index.mjs
Normal file
168
test/bankImport/index.mjs
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
import axios from "axios"
|
||||||
|
import {createClient} from "@supabase/supabase-js";
|
||||||
|
|
||||||
|
let key = process.env.SUPABASE_KEY
|
||||||
|
let url = process.env.SUPABASE_URL
|
||||||
|
let interval = process.env.INTERVAL
|
||||||
|
let goCardLessBaseUrl = process.env.GOCARDLESS_URL
|
||||||
|
let goCardLessSecretId = process.env.GOCARDLESS_SECRET_ID
|
||||||
|
let goCardLessSecretKey = process.env.GOCARDLESS_SECRET_KEY
|
||||||
|
|
||||||
|
const supabase = createClient(url,key)
|
||||||
|
|
||||||
|
let accessToken = ""
|
||||||
|
let refreshToken = ""
|
||||||
|
|
||||||
|
|
||||||
|
console.log(goCardLessBaseUrl)
|
||||||
|
|
||||||
|
|
||||||
|
let currentTenant = 1
|
||||||
|
|
||||||
|
let tenantData = (await supabase.from("tenants").select().eq("id", currentTenant)).data[0]
|
||||||
|
//console.log(tenantData)
|
||||||
|
|
||||||
|
|
||||||
|
let bankConfig = tenantData.bankConfig
|
||||||
|
|
||||||
|
//console.log(bankConfig)
|
||||||
|
|
||||||
|
//console.log(goCardLessBaseUrl + "token/new/")
|
||||||
|
let tokenData = (await axios({
|
||||||
|
url: goCardLessBaseUrl + "token/new/",
|
||||||
|
method: "POST",
|
||||||
|
data: {
|
||||||
|
"secret_id": goCardLessSecretId,
|
||||||
|
"secret_key": goCardLessSecretKey
|
||||||
|
}
|
||||||
|
})).data
|
||||||
|
|
||||||
|
accessToken = tokenData.access
|
||||||
|
refreshToken = tokenData.access
|
||||||
|
|
||||||
|
let accounts = []
|
||||||
|
|
||||||
|
const getAccounts = async () => {
|
||||||
|
let existingAccounts = (await supabase.from("bankAccounts").select()).data
|
||||||
|
|
||||||
|
let requisitionData = (await axios({
|
||||||
|
url: goCardLessBaseUrl + "requisitions/" + bankConfig.requisitions[0],
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${accessToken}`
|
||||||
|
},
|
||||||
|
method: "GET",
|
||||||
|
})).data
|
||||||
|
|
||||||
|
requisitionData.accounts.forEach(account => {
|
||||||
|
if (!accounts.includes(account)) {
|
||||||
|
accounts.push(account)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
let supabaseAccountInserts = []
|
||||||
|
|
||||||
|
async function getAccountData () {
|
||||||
|
await Promise.all(accounts.map(async (account) => {
|
||||||
|
let accountData = (await axios({
|
||||||
|
url: goCardLessBaseUrl + "accounts/" + account,
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${accessToken}`
|
||||||
|
},
|
||||||
|
method: "GET"
|
||||||
|
})).data
|
||||||
|
|
||||||
|
//console.log(accountData)
|
||||||
|
|
||||||
|
if(existingAccounts.filter(account => account.iban === accountData.iban).length === 0){
|
||||||
|
supabaseAccountInserts.push({
|
||||||
|
name: "",
|
||||||
|
iban: accountData.iban,
|
||||||
|
bankId: accountData.institution_id,
|
||||||
|
ownerName: accountData.owner_name,
|
||||||
|
tenant: currentTenant,
|
||||||
|
accountId: accountData.id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
await getAccountData()
|
||||||
|
|
||||||
|
//console.log("Start Insert")
|
||||||
|
|
||||||
|
const {data:accountInsertData,error:accountInsertError} = await supabase.from("bankAccounts").insert(supabaseAccountInserts).select()
|
||||||
|
if(accountInsertError) {
|
||||||
|
console.log(accountInsertError)
|
||||||
|
} else if(accountInsertData) {
|
||||||
|
//console.log(accountInsertData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const getTransactions = async () => {
|
||||||
|
let accountsToRequest = (await supabase.from("bankAccounts").select().eq("used", true)).data
|
||||||
|
let existingTransactions = (await supabase.from("bankStatements").select().eq("tenant", currentTenant)).data
|
||||||
|
|
||||||
|
console.log(accountsToRequest)
|
||||||
|
|
||||||
|
let supabaseTransactionInserts = []
|
||||||
|
|
||||||
|
async function getAccountTransactions () {
|
||||||
|
await Promise.all(accountsToRequest.map(async (account) => {
|
||||||
|
let transactions = (await axios({
|
||||||
|
url: goCardLessBaseUrl + "accounts/" + account.accountId + "/transactions",
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${accessToken}`
|
||||||
|
},
|
||||||
|
method: "GET"
|
||||||
|
})).data.transactions.booked
|
||||||
|
|
||||||
|
//console.log(transactions)
|
||||||
|
|
||||||
|
|
||||||
|
transactions.forEach(transaction => {
|
||||||
|
if(!transaction.creditorAccount) console.log(transaction)
|
||||||
|
|
||||||
|
let transactionData = {
|
||||||
|
date: transaction.bookingDate,
|
||||||
|
amount: transaction.transactionAmount.amount,
|
||||||
|
credName: transaction.creditorName ? transaction.creditorName : null,
|
||||||
|
credIban: transaction.creditorAccount ? transaction.creditorAccount.iban : null,
|
||||||
|
debName: transaction.debtorName,
|
||||||
|
debIban: transaction.debtorAccount.iban,
|
||||||
|
text: transaction.remittanceInformationUnstructured,
|
||||||
|
gocardlessId: transaction.internalTransactionId,
|
||||||
|
currency: transaction.transactionAmount.currency,
|
||||||
|
account: account.id,
|
||||||
|
tenant: currentTenant
|
||||||
|
}
|
||||||
|
|
||||||
|
if(existingTransactions.filter(item => item.gocardlessId === transaction.internalTransactionId).length === 0){
|
||||||
|
supabaseTransactionInserts.push(transactionData)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
await getAccountTransactions()
|
||||||
|
|
||||||
|
console.log(supabaseTransactionInserts)
|
||||||
|
|
||||||
|
const {data:transactionInsertData, error:transactionInsertError} = await supabase.from("bankStatements").insert(supabaseTransactionInserts).select()
|
||||||
|
|
||||||
|
if(transactionInsertError) {
|
||||||
|
console.log(transactionInsertError)
|
||||||
|
} else if(transactionInsertData) {
|
||||||
|
console.log(transactionInsertData)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
getAccounts()
|
||||||
|
getTransactions()
|
||||||
Reference in New Issue
Block a user