Many Changes

This commit is contained in:
2024-01-04 12:27:46 +01:00
parent 57e856c71c
commit 991cac18f2
31 changed files with 1504 additions and 297 deletions

View File

@@ -2,6 +2,8 @@
import GlobalSearch from "~/components/GlobalSearch.vue";
const user = useSupabaseUser()
//console.log(user.value)
const router = useRouter()
@@ -12,11 +14,6 @@ const tenants = (await supabase.from("tenants").select()).data
const dataStore = useDataStore()
const userProfile = (user.value ? dataStore.getProfileById(user.value.id) : {})
//console.log(userProfile)
const isLight = computed({
get () {
return colorMode.value !== 'dark'
@@ -26,6 +23,32 @@ const isLight = computed({
}
})
const userProfile = (user.value ? dataStore.getProfileById(user.value.id) : {})
//console.log(userProfile)
const showUserMenu = ref(false)
const userMenuItems = ref([
{
label: "Profil",
badge: dataStore.notifications.filter(item => item.read).length,
icon: "i-heroicons-user-20-solid"
},
{
label: "Nummernkreise",
icon: "i-heroicons-cog-8-tooth",
to: "/settings/numberRanges"
},
{
label: 'Benutzer',
icon: 'i-heroicons-user-group',
to: "/settings/users"
}
])
const viewport = useViewport()
watch(viewport.breakpoint, (newBreakpoint, oldBreakpoint) => {
@@ -56,7 +79,7 @@ const navLinks = [
icon: "i-heroicons-truck"
},
{
label: "Kontakte",
label: "Ansprechpartner",
to: "/contacts",
icon: "i-heroicons-user-group"
},
@@ -131,9 +154,14 @@ const navLinks = [
children: [
{
label: "Zeiterfassung",
to: "/timetracking",
to: "/employees/timetracking",
icon: "i-heroicons-clock"
},
{
label: "Abwesenheiten",
to: "/employees/absenceRequests",
icon: "i-heroicons-document-text"
}
]
},
{
@@ -141,15 +169,15 @@ const navLinks = [
icon: 'i-heroicons-home',
children: [
{
label: "Artikel",
to: "/products",
icon: "i-heroicons-puzzle-piece"
},
{
label: "Inventar",
label: "Steuerung",
to: "/inventory",
icon: "i-heroicons-square-3-stack-3d"
},
{
label: "Artikelstamm",
to: "/products",
icon: "i-heroicons-puzzle-piece"
},
{
label: "Lagerplätze",
to: "/inventory/spaces",
@@ -166,37 +194,7 @@ const navLinks = [
const linksForBreadcrumbs = ref([])
const generateLinks = () => {
let pathSteps = route.fullPath.split("/")
let returnArr = []
pathSteps.forEach((step,index) => {
let stepLink = navLinks.find(link => link.link == step)
if(stepLink) {
returnArr[index] = {
label: stepLink.label,
icon: stepLink.icon,
to: '/' + stepLink.link
}
}
linksForBreadcrumbs.value = returnArr
})
}
watch(
() => route.path,
() => {
generateLinks()
}
)
generateLinks()
@@ -223,6 +221,9 @@ useSeoMeta({
twitterCard: 'summary_large_image'
})
const items = [
[{
label: user.value ? user.value.email : "",
@@ -250,7 +251,7 @@ const items = [
label: 'Status',
icon: 'i-heroicons-signal'
}],*/ [{
label: 'Sign out',
label: 'Ausloggen',
icon: 'i-heroicons-arrow-left-on-rectangle',
click: async () => {
await supabase.auth.signOut()
@@ -279,8 +280,12 @@ const items = [
<UNavigationTree :links="navLinks"/>
</template>
<template #right v-if="user">
<ClientOnly>
<template #right>
<!-- <ClientOnly>
<UButton
:icon="!isLight ? 'i-heroicons-moon-20-solid' : 'i-heroicons-sun-20-solid'"
color="gray"
@@ -292,11 +297,128 @@ const items = [
<template #fallback>
<div class="w-8 h-8" />
</template>
</ClientOnly>
<UDropdown :items="items" :ui="{ item: { disabled: 'cursor-text select-text' } }" :popper="{ placement: 'bottom-start' }">
</ClientOnly>-->
<GlobalSearch/>
<!-- <UPopover :popper="{placement: 'bottom-start'}">
<ClientOnly>
<UChip
:show="dataStore.notifications.filter(notification => !notification.read).length > 0"
inset
>
<UButton
icon="i-heroicons-envelope-20-solid"
variant="ghost"
color="gray"
class="mx-2"
/>
</UChip>
<template #fallback>
<div class="w-8 h-8" />
</template>
</ClientOnly>
<template #panel>
<div class="w-60 p-3">
<UAlert
class="mb-3"
v-for="(notification,index) in dataStore.notifications"
:color="!notification.read ? 'primary' : 'grey'"
variant="outline"
:description="notification.text"
:title="notification.title"
/>
</div>
</template>
</UPopover>-->
<UButton
@click="showUserMenu = true"
variant="ghost"
color="gray"
>
<UAvatar
:alt="userProfile ? userProfile.firstName + ' ' + userProfile.lastName : '' "
icon="i-heroicons-photo"
icon="i-heroicons-user-20-solid"
:chip-color="dataStore.notifications.filter(item => !item.read).length > 0 ? 'primary' : null"
/>
</UButton>
<USlideover
v-model="showUserMenu"
>
<UCard
class="h-full"
>
<UDivider
class="my-3"
label="Menü"
/>
<UVerticalNavigation
:links="userMenuItems"
/>
<UDivider
class="my-3"
label="Benachrichtigungen"
/>
<UAlert
class="mb-3"
v-for="(notification) in dataStore.notifications"
:color="!notification.read ? 'primary' : 'white'"
:variant="!notification.read ? 'outline' : 'soft'"
:description="notification.text"
:title="notification.title"
:close-button="!notification.read ? { icon: 'i-heroicons-x-mark-20-solid', color: 'gray', variant: 'link', padded: false } : null"
@close="async () => {
const {error} = await supabase.from('notifications').update({read:true}).eq('id', notification.id)
if(error) console.log(error)
dataStore.fetchNotifications()
}"
/>
<template #footer>
<InputGroup>
<UButton
:icon="!isLight ? 'i-heroicons-moon-20-solid' : 'i-heroicons-sun-20-solid'"
color="white"
variant="outline"
aria-label="Theme"
@click="isLight = !isLight"
/>
<UButton
color="rose"
variant="outline"
@click="async () => {
showUserMenu = false
await supabase.auth.signOut()
await dataStore.clearStore()
await router.push('/login')
}"
>
Ausloggen
</UButton>
</InputGroup>
</template>
</UCard>
</USlideover>
<!-- <UDropdown :items="items" :ui="{ item: { disabled: 'cursor-text select-text' } }" :popper="{ placement: 'bottom-start' }">
<UAvatar
:alt="userProfile ? userProfile.firstName + ' ' + userProfile.lastName : '' "
icon="i-heroicons-user-20-solid"
/>
<template #account="{ item }">
@@ -315,7 +437,7 @@ const items = [
<UIcon :name="item.icon" class="flex-shrink-0 h-4 w-4 text-gray-400 dark:text-gray-500 ms-auto" />
</template>
</UDropdown>
</UDropdown>-->
</template>
</UHeader>
<UDivider />

View File

@@ -0,0 +1,110 @@
<script setup>
const showCommandPalette = ref(false)
const selectedCommand = ref("")
const commandPaletteRef = ref()
const router = useRouter()
const dataStore = useDataStore()
const openCommandPalette = () => {
showCommandPalette.value = true
console.log("Open Command Palette")
}
defineShortcuts({
meta_k: {
usingInput: true,
handler: () => {
openCommandPalette()
}
}
})
const actions = [
{
id: 'new-customer',
label: 'Kunde hinzufügen',
icon: 'i-heroicons-document-plus',
to: "/customers/create" ,
},
{
id: 'new-vendor',
label: 'Lieferant hinzufügen',
icon: 'i-heroicons-truck',
to: "/vendors/create" ,
},
{
id: 'new-contact',
label: 'Ansprechpartner hinzufügen',
icon: 'i-heroicons-user',
to: "/contacts/create" ,
},
{
id: 'new-product',
label: 'Artikel hinzufügen',
icon: 'i-heroicons-puzzle-piece',
to: "/products/create" ,
}
]
const groups = computed(() =>
[{
key: 'actions',
commands: actions
},{
key: "customers",
label: "Kunden",
commands: dataStore.customers.map(item => { return {id: item.id, label: item.name, to: `/customers/show/${item.id}`}})
},{
key: "vendors",
label: "Lieferanten",
commands: dataStore.vendors.map(item => { return {id: item.id, label: item.name, to: `/vendors/show/${item.id}`}})
},{
key: "contacts",
label: "Ansprechpartner",
commands: dataStore.contacts.map(item => { return {id: item.id, label: item.fullName, to: `/contacts/show/${item.id}`}})
},{
key: "products",
label: "Artikel",
commands: dataStore.products.map(item => { return {id: item.id, label: item.name, to: `/products/show/${item.id}`}})
}
].filter(Boolean))
function onSelect (option) {
if (option.click) {
option.click()
} else if (option.to) {
router.push(option.to)
} else if (option.href) {
window.open(option.href, '_blank')
}
showCommandPalette.value = false
}
</script>
<template>
<UButton
icon="i-heroicons-magnifying-glass"
variant="ghost"
@click="openCommandPalette"
/>
<UModal
v-model="showCommandPalette"
>
<UCommandPalette
v-model="selectedCommand"
:groups="groups"
:autoselect="false"
@update:model-value="onSelect"
ref="commandPaletteRef"
/>
</UModal>
</template>
<style scoped>
</style>

View File

@@ -10,36 +10,22 @@ const props = defineProps({
type: String
}
})
const dataStore = useDataStore()
const user = useSupabaseUser()
const supabase = useSupabaseClient()
const toast = useToast()
const {type, elementId} = props
const showAddHistoryItemModal = ref(false)
const colorMode = useColorMode()
/*const historyItems = ref([
{
user: "86e67794-0ea8-41b0-985a-1072e84f56e9",
text: "<a class='text-primary-500'>@marielesindern</a> magst du die einmal anschauen",
},
{
user: "3b795486-6b71-4ed8-a1f6-dbc52360d826",
text: "<a class='text-primary-500'>@florianfederspiel</a> Jo alles bestens",
},
{
user: "Spaces Bot",
text: "Erstellt",
}
])*/
const {profiles} = storeToRefs(useDataStore())
const {getHistoryItemsByCustomer, fetchHistoryItems} = useDataStore()
const historyItems = computed(() => {
let items = []
if(type === "customer") {
items = getHistoryItemsByCustomer(elementId)
items = dataStore.getHistoryItemsByCustomer(elementId)
} else if(type === "vendor") {
items = dataStore.getHistoryItemsByVendor(elementId)
}
return items.reverse()
@@ -68,7 +54,7 @@ const addHistoryItem = async () => {
} else {
toast.add({title: "Eintrag erfolgreich erstellt"})
showAddHistoryItemModal.value = false
await fetchHistoryItems()
await dataStore.fetchHistoryItems()
}
}
@@ -130,11 +116,11 @@ const renderText = (text) => {
:src="colorMode.value === 'light' ? '/spaces_hell.svg' : '/spaces.svg' "
/>
<UAvatar
:alt="profiles.find(profile => profile.id === item.user).fullName"
:alt="dataStore.profiles.find(profile => profile.id === item.user).fullName"
v-else
/>
<div>
<h3 v-if="item.user">{{profiles.find(profile => profile.id === item.user) ? profiles.find(profile => profile.id === item.user).fullName : ""}}</h3>
<h3 v-if="item.user">{{dataStore.profiles.find(profile => profile.id === item.user) ? dataStore.profiles.find(profile => profile.id === item.user).fullName : ""}}</h3>
<h3 v-else>Spaces Bot</h3>
<span v-html="renderText(item.text)"/><br>
<span class="text-gray-500">{{dayjs(item.created_at).format("DD:MM:YY HH:mm")}}</span>

View File

@@ -0,0 +1,21 @@
<template>
<div class="relative overflow-hidden rounded border border-dashed border-gray-400 dark:border-gray-500 opacity-75 px-4 flex items-center justify-center">
<svg class="absolute inset-0 h-full w-full stroke-gray-900/10 dark:stroke-white/10" fill="none">
<defs>
<pattern
id="pattern-5c1e4f0e-62d5-498b-8ff0-cf77bb448c8e"
x="0"
y="0"
width="10"
height="10"
patternUnits="userSpaceOnUse"
>
<path d="M-3 13 15-5M-5 5l18-18M-1 21 17 3" />
</pattern>
</defs>
<rect stroke="none" fill="url(#pattern-5c1e4f0e-62d5-498b-8ff0-cf77bb448c8e)" width="100%" height="100%" />
</svg>
<slot />
</div>
</template>

View File

@@ -27,7 +27,7 @@
"@fullcalendar/resource-timeline": "^6.1.10",
"@fullcalendar/vue3": "^6.1.10",
"@nuxt/content": "^2.9.0",
"@nuxt/ui-pro": "^0.5.0",
"@nuxt/ui-pro": "^0.6.1",
"@nuxtjs/fontaine": "^0.4.1",
"@nuxtjs/google-fonts": "^3.1.0",
"@nuxtjs/strapi": "^1.9.3",
@@ -38,6 +38,7 @@
"@zip.js/zip.js": "^2.7.32",
"axios": "^1.6.2",
"buffer": "^6.0.3",
"client-oauth2": "^4.3.3",
"dayjs": "^1.11.10",
"jsprintmanager": "^6.0.3",
"nuxt-editorjs": "^1.0.4",

View File

@@ -70,17 +70,17 @@ const cancelEditorCreate = () => {
}
}
const updateCustomer = async () => {
const updateItem = 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: "",
if(error) {
console.log(error)
}
router.push(`/contacts/show/${currentContact.id}`)
toast.add({title: "Kontakt erfolgreich gespeichert"})
dataStore.fetchContacts()
}
@@ -261,7 +261,7 @@ setupPage()
<template #footer>
<UButton
v-if="mode == 'edit'"
@click="updateCustomer"
@click="updateItem"
>
Speichern
</UButton>

View File

@@ -71,17 +71,16 @@ const cancelEditorCreate = () => {
}
}
const updateCustomer = async () => {
const updateItem = async () => {
const {error} = await supabase
.from("contracts")
.update(itemInfo.value)
.eq('id',itemInfo.value.id)
console.log(error)
mode.value = "show"
itemInfo.value = {
id: 0,
name: "",
if(error) {
console.log(error)
}
router.push(`/contracts/show/${currentContract.id}`)
toast.add({title: "Vertrag erfolgreich gespeichert"})
dataStore.fetchContracts()
}
@@ -192,7 +191,7 @@ setupPage()
<template #footer>
<UButton
v-if="mode == 'edit'"
@click="updateCustomer"
@click="updateItem"
>
Speichern
</UButton>

View File

@@ -81,14 +81,12 @@ const updateCustomer = async () => {
.from("customers")
.update(customerInfo.value)
.eq('id',customerInfo.value.id)
console.log(error)
mode.value = "show"
customerInfo.value = {
id: 0,
name: "",
infoData: {}
if(error) {
console.log(error)
}
toast.add({title: "Kunde erfolgreich gespeichert"})
router.push(`/customers/show/${currentCustomer.id}`)
dataStore.fetchCustomers()
}
@@ -121,7 +119,14 @@ setupPage()
/>
Informationen:<br>
{{currentCustomer.infoData}}<br>
<div v-if="currentCustomer.infoData">
<span v-if="currentCustomer.infoData.street">Straße + Hausnummer: {{currentCustomer.infoData.street}}<br></span>
<span v-if="currentCustomer.infoData.zip && currentCustomer.infoData.city">PLZ + Ort: {{currentCustomer.infoData.zip}} {{currentCustomer.infoData.city}}<br></span>
<span v-if="currentCustomer.infoData.tel">Telefon: {{currentCustomer.infoData.tel}}<br></span>
<span v-if="currentCustomer.infoData.email">E-Mail: {{currentCustomer.infoData.email}}<br></span>
<span v-if="currentCustomer.infoData.web">Web: {{currentCustomer.infoData.web}}<br></span>
<span v-if="currentCustomer.infoData.ustid">USt-Id: {{currentCustomer.infoData.ustid}}<br></span>
</div>
<UDivider
class="my-2"

View File

@@ -1,90 +0,0 @@
<script setup>
import { BlobReader, BlobWriter, ZipWriter } from "@zip.js/zip.js";
const downloadFolder = async (folder) => {
const supabaseClient = useSupabaseClient();
const bucket = "documents";
// Get a list of all the files in the path /my-bucket/images
/*const { data: files, error } = await supabaseClient.storage
.from(bucket)
.list(folder);
if (error) {
throw error;
}*/
let files = [
"1/Eingang/Rechnung_VRB170A0249604_2023-12-06.pdf"
]
console.log(files)
// If there are no files in the folder, throw an error
if (!files || !files.length) {
throw new Error("No files to download");
}
const promises = [];
// Download each file in the folder
files.forEach((file) => {
promises.push(
supabaseClient.storage.from(bucket).download(`${file}`)
);
});
// Wait for all the files to download
const response = await Promise.allSettled(promises);
// Map the response to an array of objects containing the file name and blob
const downloadedFiles = response.map((result, index) => {
if (result.status === "fulfilled") {
return {
name: files[index],
blob: result.value.data,
};
}
});
// Create a new zip file
const zipFileWriter = new BlobWriter("application/zip");
const zipWriter = new ZipWriter(zipFileWriter, { bufferedWrite: true });
// Add each file to the zip file
downloadedFiles.forEach((downloadedFile) => {
if (downloadedFile) {
zipWriter.add(downloadedFile.name, new BlobReader(downloadedFile.blob));
}
});
// Download the zip file
const url = URL.createObjectURL(await zipWriter.close());
const link = document.createElement("a");
link.href = url;
link.setAttribute("download", "documents.zip");
document.body.appendChild(link);
link.click();
}
</script>
<template>
<div>
<UButton
@click="downloadFolder('1/Eingang')"
>
Download
</UButton>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,256 @@
<script setup>
import dayjs from "dayjs";
definePageMeta({
middleware: "auth"
})
const dataStore = useDataStore()
const supabase = useSupabaseClient()
const route = useRoute()
const router = useRouter()
const toast = useToast()
const id = ref(route.params.id ? route.params.id : null )
let currentItem = null
//Working
const mode = ref(route.params.mode || "show")
const itemInfo = ref({
approved: null
})
const states = ["Offen","Genehmigt", "Abgelehnt"]
const absenceReasons = [
"Elternzeit",
"Kind krank - Kinderbetreuung",
"Krankheit",
"Krankheit 1 Tag (mit Attest)",
"Krankheit ab 2. Tag (mit Attest)",
"Mutterschutz",
"Sonderurlaub (bezahlt)",
"Überstundenausgleich",
"Unbezahlter Urlaub",
"Urlaub"
]
//Functions
const setupPage = () => {
if(mode.value === "show" || mode.value === "edit"){
currentItem = dataStore.getAbsenceRequestById(Number(useRoute().params.id))
}
if(mode.value === "edit") itemInfo.value = currentItem
}
const createItem = async () => {
const {data,error} = await supabase
.from("absenceRequests")
.insert([itemInfo.value])
.select()
if(error) {
console.log(error)
} else {
mode.value = "show"
itemInfo.value = {
id: 0,
}
toast.add({title: "Abwesenheit erfolgreich erstellt"})
await dataStore.fetchAbsenceRequests()
router.push(`/employees/absenceRequests/show/${data[0].id}`)
setupPage()
}
}
const editItem = async () => {
router.push(`/employees/absenceRequests/edit/${currentItem.id}`)
setupPage()
}
const cancelEditorCreate = () => {
mode.value = "show"
itemInfo.value = {
id: 0,
infoData: {}
}
}
const updateItem = async () => {
const {error} = await supabase
.from("absenceRequests")
.update(itemInfo.value)
.eq('id',itemInfo.value.id)
if(error) {
console.log(error)
}
router.push(`/employees/absenceRequests/show/${currentItem.id}`)
toast.add({title: "Abwesenheit erfolgreich gespeichert"})
dataStore.fetchAbsenceRequests()
}
setupPage()
</script>
<template>
<div>
<UCard v-if="currentItem && mode == 'show'" >
<template #header>
<UBadge
v-if="currentItem.approved === 'Offen'"
color="blue"
>{{currentItem.approved}}</UBadge>
<UBadge
v-else-if="currentItem.approved === 'Genehmigt'"
color="primary"
>{{currentItem.approved}}</UBadge>
<UBadge
v-else-if="currentItem.approved === 'Abgelehnt'"
color="rose"
>{{currentItem.approved}}</UBadge>
{{currentItem.reason}}
</template>
<span>Mitarbeiter: {{dataStore.profiles.find(item => item.id === currentItem.user) ? dataStore.profiles.find(item => item.id === currentItem.user).fullName : ""}}<br></span>
<span>Start: {{dayjs(currentItem.start).format("DD.MM.YYYY")}}<br></span>
<span>Ende: {{dayjs(currentItem.end).format("DD.MM.YYYY")}}<br></span>
Notizen:<br>
{{currentItem.note}}<br>
<template #footer>
<UButton
v-if="mode == 'show' && currentItem.id"
@click="editItem"
>
Bearbeiten
</UButton>
<UButton
color="red"
class="ml-2"
disabled
>
Archivieren
</UButton>
<!-- TODO: Kunde archivieren -->
</template>
</UCard>
<UCard v-else-if="mode == 'edit' || mode == 'create'" >
<template #header v-if="mode === 'edit'">
{{itemInfo.reason}}
</template>
<UFormGroup
label="Status:"
>
<USelectMenu
v-model="itemInfo.approved"
:options="states"
option-attribute="label"
value-attribute="value"
/>
</UFormGroup>
<UFormGroup
label="Grund:"
>
<USelectMenu
v-model="itemInfo.reason"
:options="absenceReasons"
/>
</UFormGroup>
<UFormGroup
label="Mitarbeiter:"
>
<USelectMenu
v-model="itemInfo.user"
:options="dataStore.profiles"
option-attribute="fullName"
value-attribute="id"
searchable
:search-attributes="['fullName']"
>
<template #label>
{{dataStore.profiles.find(item => item.id === itemInfo.user) ? dataStore.profiles.find(item => item.id === itemInfo.user).fullName : "Mitarbeiter auswählen"}}
</template>
</USelectMenu>
</UFormGroup>
<UFormGroup label="Start:">
<UPopover :popper="{ placement: 'bottom-start' }">
<UButton icon="i-heroicons-calendar-days-20-solid" :label="itemInfo.start ? dayjs(itemInfo.start).format('DD.MM.YYYY') : 'Datum auswählen'" />
<template #panel="{ close }">
<LazyDatePicker v-model="itemInfo.start" @close="close" />
</template>
</UPopover>
</UFormGroup>
<UFormGroup label="Ende:">
<UPopover :popper="{ placement: 'bottom-start' }">
<UButton icon="i-heroicons-calendar-days-20-solid" :label="itemInfo.end ? dayjs(itemInfo.end).format('DD.MM.YYYY') : 'Datum auswählen'" />
<template #panel="{ close }">
<LazyDatePicker v-model="itemInfo.end" @close="close" />
</template>
</UPopover>
</UFormGroup>
<UFormGroup
label="Notizen:"
>
<UTextarea
v-model="itemInfo.note"
/>
</UFormGroup>
<template #footer>
<UButton
v-if="mode == 'edit'"
@click="updateItem"
>
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>

View File

@@ -0,0 +1,84 @@
<template>
<div id="main">
<UButton @click="router.push(`/employees/absenceRequests/create/`)">+ Abwesenheit</UButton>
<UTable
:rows="dataStore.absenceRequests"
:columns="columns"
@select="selectItem"
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
>
<template #approved-data="{row}">
<span v-if="!row.approved">
Genehmigung offen
</span>
<span
v-else-if="row.approved"
class="text-primary"
>
Genemigt
</span>
<span
v-else
class="text-rose"
>
Abgelehnt
</span>
</template>
<template #user-data="{row}">
{{dataStore.profiles.find(profile => profile.id === row.user) ? dataStore.profiles.find(profile => profile.id === row.user).fullName : ""}}
</template>
</UTable>
</div>
</template>
<script setup>
definePageMeta({
middleware: "auth"
})
const dataStore = useDataStore()
const router = useRouter()
const mode = ref("show")
const columns = [
{
key: "approved",
label: "Genehmigt",
sortable: true
},
{
key: "user",
label: "Mitarbeiter",
sortable: true
},{
key: "reason",
label: "Grund",
sortable: true
},{
key: "start",
label: "Start",
sortable: true
},{
key: "end",
label: "Ende",
sortable: true
},{
key: "note",
label: "Notizen",
sortable: true
}
]
const selectItem = (item) => {
router.push(`/employees/absenceRequests/show/${item.id} `)
}
</script>
<style scoped>
</style>

View File

@@ -3,60 +3,71 @@ definePageMeta({
middleware: "auth"
})
const dataStore = useDataStore()
const supabase = useSupabaseClient()
const {spaces, movements, products} = storeToRefs(useDataStore())
const searchinput = ref("")
const mode = ref("")
const router = useRouter()
const mode = ref("incoming")
const toast = useToast()
const showReader = ref(false)
const inventoryChangeData = ref({
productId: "",
spaceId: "",
quantity: 0
quantity: 1
})
const createMovement = async () => {
if(mode.value === 'incoming'){
//await create('movements', inventoryChangeData.value)
const {data,error} = await supabase
.from("movements")
.insert([inventoryChangeData.value])
.select()
if(mode.value === '' || !checkSpaceId(inventoryChangeData.value.spaceId) || !checkArticle(inventoryChangeData.value.productId)){
console.log(error)
} else {
if(mode.value === 'incoming'){
const {error} = await supabase
.from("movements")
.insert([inventoryChangeData.value])
.select()
if(error) console.log(error)
} else if (mode.value === 'outgoing'){
inventoryChangeData.value.quantity *= -1
//await create('movements', inventoryChangeData.value)
} else if (mode.value === 'outgoing'){
inventoryChangeData.value.quantity *= -1
const {data,error} = await supabase
.from("movements")
.insert([inventoryChangeData.value])
.select()
const {error} = await supabase
.from("movements")
.insert([inventoryChangeData.value])
.select()
if(error) console.log(error)
} else if (mode.value === 'change'){}
console.log(error)
} else if (mode.value === 'change'){}
inventoryChangeData.value = {
productId: 0,
spaceId: 0,
quantity: 0
inventoryChangeData.value = {
productId: "",
spaceId: "",
quantity: 1
}
dataStore.fetchMovements()
}
}
defineShortcuts({
meta_enter: {
usingInput: true,
handler: () => {
createMovement()
}
}
})
function checkArticle(productId) {
return products.value.filter(product =>product.ean === productId).length > 0;
return dataStore.products.filter(product =>product.id === productId).length > 0;
}
function checkSpaceId(spaceId) {
return spaces.value.filter(space => Number(space.spaceNumber) === Number(spaceId)).length > 0;
return dataStore.spaces.filter(space => space.id === spaceId).length > 0;
}
function changeFocusToSpaceId() {
@@ -73,42 +84,66 @@ function changeFocusToQuantity() {
<div id="main">
<div class="my-3">
<UButton @click="mode = 'incoming'" class="ml-3" >Wareneingang</UButton>
<UButton @click="mode = 'outgoing'" class="ml-1">Warenausgang</UButton>
<UButton @click="mode = 'change'" class="ml-1" disabled>Umlagern</UButton>
<UButton
@click="mode = 'incoming'"
class="ml-3"
:variant="mode === 'incoming' ? 'solid' : 'outline'"
>Wareneingang</UButton>
<UButton
@click="mode = 'outgoing'"
class="ml-1"
:variant="mode === 'outgoing' ? 'solid' : 'outline'"
>Warenausgang</UButton>
<!-- <UButton
@click="mode = 'change'"
class="ml-1"
disabled
:variant="mode === 'change' ? 'solid' : 'outline'"
>Umlagern</UButton>-->
</div>
<UFormGroup
label="Artikel:"
class="mt-3 w-80"
>
<UInput
<USelectMenu
:options="dataStore.products"
option-attribute="name"
value-attribute="id"
variant="outline"
searchable
:search-attributes="['name','ean', 'barcode']"
:color="checkArticle(inventoryChangeData.productId) ? 'primary' : 'rose'"
v-model="inventoryChangeData.productId"
v-on:keyup.enter="changeFocusToSpaceId"
/>
v-on:select="changeFocusToSpaceId"
>
<template #label>
{{dataStore.products.find(product => product.id === inventoryChangeData.productId) ? dataStore.products.find(product => product.id === inventoryChangeData.productId).name : "Bitte Artikel auswählen"}}
</template>
</USelectMenu>
</UFormGroup>
<UFormGroup
label="Lagerplatz:"
class="mt-3 w-80"
><!--.map(space => {return {id: space.id, name: space.spaceNumber}}-->
<!-- <USelectMenu
:options="spaces"
>
<USelectMenu
:options="dataStore.spaces"
searchable
option-attribute="spaceNumber"
option-attribute="description"
:color="checkSpaceId(inventoryChangeData.spaceId) ? 'primary' : 'rose'"
v-model="inventoryChangeData.spaceId"
v-on:select="changeFocusToQuantity"
value-attribute="id"
/>-->
<UInput
v-model="inventoryChangeData.spaceId"
:color="checkSpaceId(inventoryChangeData.spaceId) ? 'primary' : 'rose'"
id="spaceIdInput"
v-on:keyup.enter="changeFocusToQuantity"
/>
>
<template #label>
{{dataStore.spaces.find(space => space.id === inventoryChangeData.spaceId) ? dataStore.spaces.find(space => space.id === inventoryChangeData.spaceId).description : "Kein Lagerplatz ausgewählt"}}
</template>
</USelectMenu>
</UFormGroup>
<UFormGroup

View File

@@ -5,7 +5,7 @@ definePageMeta({
middleware: "auth"
})
//
const dataStore = useDataStore()
const supabase = useSupabaseClient()
const route = useRoute()
const router = useRouter()
@@ -13,9 +13,6 @@ const toast = useToast()
const id = ref(route.params.id ? route.params.id : null )
const numberRange = useNumberRange("spaces")
//Store
const {spaces,movements,products,units,ownTenant, numberRanges} = storeToRefs(useDataStore())
const {fetchSpaces, getSpaceById, movementsBySpace, getProductById, fetchNumberRanges} = useDataStore()
let currentItem = ref(null)
@@ -26,7 +23,7 @@ const mode = ref(route.params.mode || "show")
const itemInfo = ref({
spaceNumber: ""
})
const spaceTypes = ["Regalplatz", "Kiste", "Palettenplatz"]
const spaceTypes = ["Regalplatz", "Kiste", "Palettenplatz", "Sonstiges"]
const spaceProducts = ref([])
const spaceMovements = ref([])
@@ -34,12 +31,12 @@ const spaceMovements = ref([])
const setupPage = async () => {
console.log("Called Setup")
if(mode.value === "show" || mode.value === "edit"){
currentItem.value = await getSpaceById(Number(useRoute().params.id))
currentItem.value = await dataStore.getSpaceById(Number(useRoute().params.id))
spaceMovements.value = await movementsBySpace(currentItem.value.id)
spaceMovements.value = await dataStore.getMovementsBySpace(currentItem.value.id)
spaceProducts.value = []
spaceMovements.value.forEach(movement => {
if(spaceProducts.value.filter(product => product.id === movement.productId).length === 0) spaceProducts.value.push(getProductById(movement.productId))
if(spaceProducts.value.filter(product => product.id === movement.productId).length === 0) spaceProducts.value.push(dataStore.getProductById(movement.productId))
})
}
@@ -63,7 +60,7 @@ const createItem = async () => {
console.log(data[0])
mode.value = "show"
toast.add({title: "Lagerplatz erfolgreich erstellt"})
await fetchSpaces()
await dataStore.fetchSpaces()
router.push(`/inventory/spaces/show/${data[0].id}`)
@@ -93,7 +90,7 @@ const updateItem = async () => {
mode.value = "show"
itemInfo.value = {}
toast.add({title: "Lagerplatz erfolgreich gespeichert"})
fetchSpaces()
dataStore.fetchSpaces()
}
}
@@ -111,7 +108,7 @@ const printSpaceLabel = async () => {
axios
.post(`http://${ownTenant.value.labelPrinterIp}/pstprnt`, `^XA^FO10,20^BCN,100^FD${currentItem.value.spaceNumber}^XZ` )
.post(`http://${dataStore.ownTenant.value.labelPrinterIp}/pstprnt`, `^XA^FO10,20^BCN,100^FD${currentItem.value.spaceNumber}^XZ` )
.then(console.log)
.catch(console.log)
}
@@ -149,7 +146,7 @@ setupPage()
<tr v-for="product in spaceProducts">
<td>{{product.name}}</td>
<td>{{getSpaceProductCount(product.id)}}</td>
<td>{{units.find(unit => unit.id === product.unit).name}}</td>
<td>{{dataStore.units.find(unit => unit.id === product.unit).name}}</td>
</tr>
</table>

View File

@@ -72,12 +72,13 @@ const updateItem = async () => {
.from("jobs")
.update(itemInfo.value)
.eq('id',itemInfo.value.id)
console.log(error)
mode.value = "show"
itemInfo.value = {
id: 0,
title: ""
if(error) {
console.log(error)
}
router.push(`/jobs/show/${currentItem.id}`)
toast.add({title: "Job erfolgreich gespeichert"})
dataStore.fetchJobs()
}

View File

@@ -8,7 +8,11 @@
:columns="columns"
@select="selectJob"
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
/>
>
<template #customer-data="{row}">
{{dataStore.customers.find(customer => customer.id === row.customer) ? dataStore.customers.find(customer => customer.id === row.customer).name : "" }}
</template>
</UTable>
</div>
</template>

View File

@@ -4,6 +4,7 @@ import resourceTimelinePlugin from '@fullcalendar/resource-timeline'
import deLocale from '@fullcalendar/core/locales/de'
import interactionPlugin from '@fullcalendar/interaction'
import listPlugin from '@fullcalendar/list';
import dayjs from "dayjs";
const viewport = useViewport()
@@ -19,6 +20,7 @@ const eventTypes = dataStore.getEventTypes
const events = dataStore.getEvents
const openNewEventModal = ref(false)
const showEventModal = ref(false)
const newEventData = ref({
resourceId: "",
resourceType: "",
@@ -27,6 +29,9 @@ const newEventData = ref({
start: "",
end: null
})
const selectedEvent = ref({})
const createEvent = async () => {
const {data,error} = await supabase
.from("events")
@@ -76,6 +81,9 @@ const calendarOptionsTimeline = reactive({
openNewEventModal.value = true
},
eventClick: function (info){
selectedEvent.value = info.event
showEventModal.value = true
console.log(info)
},
resourceGroupField: "type",
@@ -153,7 +161,28 @@ const calendarOptionsTimeline = reactive({
</UCard>
</UModal>
<UModal
v-model="showEventModal"
>
<UCard>
<template #header>
{{selectedEvent.title}}
</template>
Start: {{dayjs(selectedEvent.startStr).format("DD.MM.YYYY HH:mm")}}<br>
Ende: {{dayjs(selectedEvent.endStr).format("DD.MM.YYYY HH:mm")}}
<DevOnly>
<UDivider class="my-3"/>
{{selectedEvent}}
</DevOnly>
</UCard>
</UModal>
<div v-if="viewport.isLessThan('tablet')">
<FullCalendar
:options="calendarOptionsList"

View File

@@ -107,10 +107,12 @@ setupPage()
{{tag}}
</UBadge>
<UDivider
class="my-2"
/>
<span v-if="currentProduct.purchasePrice">Einkaufspreis: {{Number(currentProduct.purchasePrice).toFixed(2)}} <br></span>
Bestand: {{dataStore.getStockByProductId(currentProduct.id)}} {{dataStore.units.find(unit => unit.id === currentProduct.unit) ? dataStore.units.find(unit => unit.id === currentProduct.unit).name : ""}}
<DevOnly>
@@ -193,6 +195,26 @@ setupPage()
v-model="itemInfo.ean"
/>
</UFormGroup>
<UFormGroup
label="Barcode:"
>
<UInput
v-model="itemInfo.barcode"
/>
</UFormGroup>
<UFormGroup
label="Einkaufspreis:"
>
<UInput
v-model="itemInfo.purchasePrice"
type="number"
steps="0.01"
>
<template #trailing>
<span class="text-gray-500 dark:text-gray-400 text-xs">EUR</span>
</template>
</UInput>
</UFormGroup>
<template #footer>

View File

@@ -18,6 +18,12 @@
@select="selectItem"
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
>
<template #stock-data="{row}">
{{`${dataStore.getStockByProductId(row.id)} ${(dataStore.units.find(unit => unit.id === row.unit) ? dataStore.units.find(unit => unit.id === row.unit).name : "")}`}}
</template>
<template #purchasePrice-data="{row}">
{{row.purchasePrice ? Number(row.purchasePrice).toFixed(2) + " €" : ""}}
</template>
<template #tags-data="{row}">
<UBadge
v-if="row.tags.length > 0"
@@ -48,24 +54,33 @@ const router = useRouter()
const itemColumns = [
{
key: "id",
label: "Id"
key: "stock",
label: "Bestand"
},
{
key: "name",
label: "Name"
label: "Name",
sortable: true
},
{
key: "manufacturer",
label: "Hersteller",
sortable: true
},
{
key: "unit",
label: "Einheit"
label: "Einheit",
sortable: true
},
{
key: "purchasePrice",
label: "Einkaufspreis",
sortable: true
},
{
key: "tags",
label: "Tags"
label: "Tags",
sortable: true
}
]

View File

@@ -8,10 +8,16 @@ import * as dayjs from 'dayjs'
const supabase = useSupabaseClient()
const user = useSupabaseUser()
const route = useRoute()
const router = useRouter()
const dataStore = useDataStore()
const {getProjectById, getFormSubmitsWithLabelProp, getTimesByProjectId, getDocumentTags, getDocumentsByProjectId, fetchDocuments} = useDataStore()
const {forms, formSubmits, times, profiles,documents} = storeToRefs(useDataStore())
fetchDocuments()
const currentProject = getProjectById(Number(route.params.id))
@@ -74,6 +80,9 @@ const tabItems = [
{
key: "phases",
label: "Phasen"
},{
key: "tasks",
label: "Aufgaben"
},{
key: "forms",
label: "Formulare"
@@ -235,6 +244,17 @@ const phaseInfo = ref({
</a>
</div>-->
</div>
<div v-if="item.key === 'tasks'" class="space-y-3">
<UTable
:rows="dataStore.getTasksByProjectId(currentProject.id)"
@select="(row) => {
router.push(`/tasks/show/${row.id}`)
}"
>
</UTable>
</div>
<div v-else-if="item.key === 'forms'" class="space-y-3">
<UButton

View File

@@ -31,6 +31,9 @@ const resources = {
vendors: {
label: "Lieferanten"
},
products: {
label: "Artikel"
},
spaces: {
label: "Lagerplätze"
}

View File

@@ -1,15 +0,0 @@
<script setup lang="ts">
definePageMeta({
middleware: "auth"
})
const dataStore = useDataStore()
</script>
<template>
{{dataStore.profiles}}
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,206 @@
<script setup>
definePageMeta({
middleware: "auth"
})
//TODO: Build User Page
const dataStore = useDataStore()
const supabase = useSupabaseClient()
const route = useRoute()
const router = useRouter()
const toast = useToast()
const id = ref(route.params.id ? route.params.id : null )
let currentItem = null
//Working
const mode = ref(route.params.mode || "show")
const itemInfo = ref({})
//Functions
const setupPage = () => {
if(mode.value === "show" || mode.value === "edit"){
currentItem = dataStore.getProfileById(useRoute().params.id)
}
if(mode.value === "edit") itemInfo.value = currentItem
}
/*const createItem = async () => {
const {data,error} = await supabase
.from("profiles")
.insert([itemInfo.value])
.select()
if(error) {
console.log(error)
} else {
mode.value = "show"
itemInfo.value = {
id: 0,
title: "",
}
toast.add({title: "Job erfolgreich erstellt"})
await dataStore.fetchJobs()
router.push(`/jobs/show/${data[0].id}`)
setupPage()
}
}*/
const editItem = async () => {
router.push(`/users/edit/${currentItem.id}`)
setupPage()
}
const cancelEditorCreate = () => {
mode.value = "show"
itemInfo.value = {
id: 0,
infoData: {}
}
}
const updateItem = async () => {
const {error} = await supabase
.from("jobs")
.update(itemInfo.value)
.eq('id',itemInfo.value.id)
if(error) {
console.log(error)
}
router.push(`/jobs/show/${currentItem.id}`)
toast.add({title: "Job erfolgreich gespeichert"})
dataStore.fetchJobs()
}
setupPage()
</script>
<template>
<div>
<UCard v-if="currentItem && mode == 'show'" >
<template #header>
<UBadge>
{{currentItem.state}}
</UBadge>
{{currentItem.title}}
</template>
Beschreibung:<br>
{{currentItem.description}}<br>
<template #footer>
<UButton
v-if="mode == 'show' && currentItem.id"
@click="editItem"
>
Bearbeiten
</UButton>
<UButton
color="red"
class="ml-2"
disabled
>
Archivieren
</UButton>
<!-- TODO: Kunde archivieren -->
</template>
</UCard>
<UCard v-else-if="mode == 'edit' || mode == 'create'" >
<template #header>
{{itemInfo.title}}
</template>
<UFormGroup
label="Titel:"
>
<UInput
v-model="itemInfo.title"
/>
</UFormGroup>
<UFormGroup
label="Status:"
>
<USelectMenu
v-model="itemInfo.state"
:options="states"
/>
</UFormGroup>
<UFormGroup
label="Kundennummer:"
>
<USelectMenu
v-model="itemInfo.customer"
:options="dataStore.customers"
option-attribute="name"
value-attribute="id"
searchable
:search-attributes="['name']"
>
<template #label>
{{dataStore.customers.find(customer => customer.id === itemInfo.customer) ? dataStore.customers.find(customer => customer.id === itemInfo.customer).name : "Kunde auswählen"}}
</template>
</USelectMenu>
</UFormGroup>
<UFormGroup
label="Beschreibung:"
>
<UTextarea
v-model="itemInfo.description"
/>
</UFormGroup>
<template #footer>
<UButton
v-if="mode == 'edit'"
@click="updateItem"
>
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>

View File

@@ -0,0 +1,51 @@
<script setup lang="ts">
definePageMeta({
middleware: "auth"
})
const dataStore = useDataStore()
const router = useRouter()
const columns = [
{
key:"id",
label: "Id"
},
{
key:"firstName",
label: "Vorname:",
},
{
key: "lastName",
label: "Nachname:"
},
{
key: "username",
label: "Benutzername"
},
{
key: "role",
label: "Rolle"
}
]
</script>
<template>
<UTable
:rows="dataStore.profiles"
:columns="columns"
@select="(item) => router.push(`/settings/users/show/${item.id}`)"
>
</UTable>
<DevOnly>
{{dataStore.profiles}}
</DevOnly>
</template>
<style scoped>
</style>

View File

@@ -45,8 +45,7 @@
:options="dataStore.profiles"
value-attribute="id"
option-attribute="fullName"
v-model="createTaskData.users"
multiple
v-model="createTaskData.user"
/>
</UFormGroup>
@@ -87,6 +86,19 @@
v-model="taskData.categorie"
/>
</UFormGroup>
<UFormGroup
label="Projekt:"
>
<USelectMenu
:options="dataStore.projects"
@change="updateTask"
v-model="taskData.project"
option-attribute="name"
value-attribute="id"
searchable
:search-attributes="['name']"
/>
</UFormGroup>
<UFormGroup
label="Benutzer ändern:"
@@ -134,6 +146,9 @@
<template #user-data="{row}">
{{dataStore.profiles.find(profile => profile.id === row.user ) ? dataStore.profiles.find(profile => profile.id === row.user ).fullName : row.user}}
</template>
<template #project-data="{row}">
{{dataStore.projects.find(item => item.id === row.project) ? dataStore.projects.find(item => item.id === row.project).name : "" }}
</template>
</UTable>
@@ -179,6 +194,11 @@ const taskColumns = [
key:"categorie",
label: "Kategorie:",
sortable: true
},
{
key:"project",
label: "Projekt:",
sortable: true
}
]

View File

@@ -72,7 +72,7 @@ const cancelEditorCreate = () => {
}
}
const updateCustomer = async () => {
const updateItem = async () => {
const {error} = await supabase
.from("vehicles")
.update(itemInfo.value)
@@ -80,13 +80,8 @@ const updateCustomer = async () => {
if(error) {
console.log(error)
} else {
mode.value = "show"
itemInfo.value = {
id: 0,
name: "",
licensePlate: "",
type: ""
}
router.push(`/vehicles/show/${currentItem.id}`)
toast.add({title: "Fahrzeug erfolgreich gespeichert"})
dataStore.fetchVehicles()
}
@@ -187,7 +182,7 @@ setupPage()
<template #footer>
<UButton
v-if="mode == 'edit'"
@click="updateCustomer"
@click="updateItem"
>
Speichern
</UButton>

View File

@@ -69,6 +69,97 @@
/>
</UFormGroup>
<div
class="my-3"
v-for="(item,index) in itemInfo.accounts"
>
<UFormGroup
label="Kategorie"
class=" mb-3"
>
<USelectMenu
:options="dataStore.accounts"
option-attribute="label"
value-attribute="id"
searchable
:search-attributes="['label']"
searchable-placeholder="Suche..."
v-model="item.account"
>
<template #label>
{{dataStore.accounts.find(account => account.id === item.account) ? dataStore.accounts.find(account => account.id === item.account).label : "Keine Kategorie ausgewählt" }}
</template>
</USelectMenu>
</UFormGroup>
<InputGroup>
<UFormGroup
label="Steuer"
class="w-32"
:help="`Betrag: ${String(item.amountTax).replace('.',',')} €`"
>
<USelectMenu
:options="[19,7,0]"
v-model="item.taxType"
>
<template #label>
{{item.taxType}} %
</template>
</USelectMenu>
</UFormGroup>
<UFormGroup
label="Gesamtbetrag exkl. Steuer in EUR"
class="flex-auto"
:help="`Betrag inkl. Steuern: ${String(Number(calculateWithTax(item)) + Number(item.amountNet)).replace('.',',')} €` "
>
<UInput
type="number"
step="0.01"
v-model="item.amountNet"
>
<template #trailing>
<span class="text-gray-500 dark:text-gray-400 text-xs">EUR</span>
</template>
</UInput>
</UFormGroup>
</InputGroup>
<UButton
class="mt-3"
@click="itemInfo.accounts = [...itemInfo.accounts.slice(0,index+1),{account:null, amountNet: null, amountTax:null, taxType: null} , ...itemInfo.accounts.slice(index+1)]"
>
Position hinzufügen
</UButton>
<UButton
v-if="index !== 0"
class="mt-3"
variant="ghost"
color="rose"
@click="itemInfo.accounts = itemInfo.accounts.filter((account,itemIndex) => itemIndex !== index)"
>
Position entfernen
</UButton>
</div>
<!--
<UFormGroup label="Kategorie:" required>
<USelectMenu
:options="dataStore.accounts"
option-attribute="label"
value-attribute="number"
searchable
:search-attributes="['label']"
v-model="itemInfo.account"
>
</USelectMenu>
</UFormGroup>
<UFormGroup label="Betrag:" required>
<UInput
type="number"
@@ -79,7 +170,7 @@
<span class="text-gray-500 dark:text-gray-400 text-xs">EUR</span>
</template>
</UInput>
</UFormGroup>
</UFormGroup>-->
<InputGroup class="mt-3">
<UButton
@@ -117,6 +208,48 @@ let currentDocument = ref(null)
//Working
const mode = ref(route.params.mode || "show")
const types = [
{
number: "3200",
label: "Wareneinkauf",
description: "Der Einkauf von Produkten umfasst auch die Kosten für die Bearbeitung/Verarbeitung und den Handel"
},{
number: "3000",
label: "Materialeinkauf",
description: "Hierzu gehören sämtliche Kosten, die im Rahmen der Produktion für Roh, Hilfs- und Betriebsstoffe anfallen."
},{
number: "3800",
label: "Bezugsnebenkosten",
description: "Dazu zählen alle Beschaffungskosten für Material und Waren bzw. Produkte"
},{
number: "4930",
label: "Bürobedarf",
description: ""
},{
number: "4964",
label: "Lizenzen und Konzessionen",
description: ""
},{
number: "4925",
label: "Internet",
description: ""
},{
number: "4920",
label: "Telekommunikation",
description: ""
},{
number: "4530",
label: "Kraftstoff/Ladestrom",
description: ""
},{
number: "4969",
label: "Müllgebühren",
description: ""
}
]
//Functions
const setupPage = async () => {
@@ -139,9 +272,24 @@ const itemInfo = ref({
dueDate: null,
paymentType: "",
description: "",
state: "Entwurf"
state: "Entwurf",
accounts: [
{
account: null,
amountNet: null,
amountTax: null,
taxType: null
}
]
})
const calculateWithTax = (item) => {
item.amountTax = Number((item.amountNet / 100 * Number(item.taxType)).toFixed(2))
return item.amountTax
}
const updateItem = async () => {
const {error} = await supabase
.from("vendorInvoices")

View File

@@ -42,7 +42,7 @@
{{row.dueDate ? dayjs(row.dueDate).format("DD.MM.YY") : ''}}
</template>
<template #amount-data="{row}">
{{row.amount ? row.amount.toFixed(2) + ' €' : ''}}
{{getRowAmount(row) === 0 ? '' : `${getRowAmount(row)}`}}
</template>
</UTable>
@@ -101,6 +101,17 @@ const selectItem = (item) => {
router.push(`/vendorinvoices/edit/${item.id} `)
}
const getRowAmount = (row) => {
let amount = 0
row.accounts.forEach(account => {
amount += account.amountNet
amount += account.amountTax
})
return amount
}
const searchString = ref('')

View File

@@ -1,4 +1,6 @@
<script setup>
import HistoryDisplay from "~/components/HistoryDisplay.vue";
definePageMeta({
middleware: "auth"
})
@@ -9,6 +11,7 @@ const route = useRoute()
const router = useRouter()
const toast = useToast()
const id = ref(route.params.id ? route.params.id : null )
const numberRange = useNumberRange("vendors")
let currentItem = null
@@ -16,7 +19,9 @@ let currentItem = null
//Working
const mode = ref(route.params.mode || "show")
const itemInfo = ref({})
const itemInfo = ref({
infoData: {}
})
//Functions
const setupPage = () => {
@@ -31,6 +36,9 @@ const setupPage = () => {
}
const createItem = async () => {
if(!itemInfo.value.vendorNumber) itemInfo.value.vendorNumber = await numberRange.useNextNumber()
const {data,error} = await supabase
.from("vendors")
.insert([itemInfo.value])
@@ -66,9 +74,8 @@ const updateItem = async () => {
if(error) {
console.log(error)
} else {
mode.value = "show"
itemInfo.value = {}
toast.add({title: "Lieferant erfolgreich gespeichert"})
router.push(`/vendors/show/${currentItem.id}`)
dataStore.fetchVendors()
}
@@ -87,9 +94,29 @@ setupPage()
{{currentItem.name}}
</template>
{{currentItem}}
Information: <br>
<div v-if="currentItem.infoData">
<span v-if="currentItem.infoData.street">Straße + Hausnummer: {{currentItem.infoData.street}}<br></span>
<span v-if="currentItem.infoData.zip && currentItem.infoData.city">PLZ + Ort: {{currentItem.infoData.zip}} {{currentItem.infoData.city}}<br></span>
<span v-if="currentItem.infoData.tel">Telefon: {{currentItem.infoData.tel}}<br></span>
<span v-if="currentItem.infoData.email">E-Mail: {{currentItem.infoData.email}}<br></span>
<span v-if="currentItem.infoData.web">Web: {{currentItem.infoData.web}}<br></span>
<span v-if="currentItem.infoData.ustid">USt-Id: {{currentItem.infoData.ustid}}<br></span>
</div>
<UDivider class="my-3"/>
<div v-if="dataStore.getContactsByVendorId(currentItem.id).length > 0">
Kontakte: <br>
<ul>
<li
v-for="contact in dataStore.getContactsByVendorId(currentItem.id)"
>
<router-link :to="'/contacts/show/' + contact.id">{{contact.salutation}} {{contact.fullName}} - {{contact.role}}</router-link>
</li>
</ul>
</div>
<template #footer>
<UButton
@@ -110,8 +137,8 @@ setupPage()
</UCard>
<UCard v-else-if="mode == 'edit' || mode == 'create'" >
<template #header>
<UCard v-else-if="mode === 'edit' || mode === 'create'" >
<template #header v-if="mode === 'edit'">
{{itemInfo.name}}
</template>
@@ -127,6 +154,57 @@ setupPage()
>
<UInput
v-model="itemInfo.vendorNumber"
placeholder="Leer lassen für automatisch generierte Nummer"
/>
</UFormGroup>
<UFormGroup
label="Straße + Hausnummer"
>
<UInput
v-model="itemInfo.infoData.street"
/>
</UFormGroup>
<UFormGroup
label="Postleitzahl"
>
<UInput
v-model="itemInfo.infoData.zip"
/>
</UFormGroup>
<UFormGroup
label="Ort"
>
<UInput
v-model="itemInfo.infoData.city"
/>
</UFormGroup>
<UFormGroup
label="Telefon:"
>
<UInput
v-model="itemInfo.infoData.tel"
/>
</UFormGroup>
<UFormGroup
label="E-Mail:"
>
<UInput
v-model="itemInfo.infoData.email"
/>
</UFormGroup>
<UFormGroup
label="Webseite:"
>
<UInput
v-model="itemInfo.infoData.web"
/>
</UFormGroup>
<UFormGroup
label="USt-Id:"
>
<UInput
v-model="itemInfo.infoData.ustid"
/>
</UFormGroup>
@@ -154,6 +232,12 @@ setupPage()
</template>
</UCard>
<HistoryDisplay
type="vendor"
v-if="currentItem"
:element-id="currentItem.id"
/>
</div>
</template>

View File

@@ -0,0 +1,3 @@
export default defineEventHandler((event) => {
event.context.auth = { user: 123 }
})

View File

@@ -1,6 +1,5 @@
import {defineStore} from 'pinia'
import {createClient} from '@supabase/supabase-js'
import * as dayJs from "dayjs"
//const supabase = createClient('https://uwppvcxflrcsibuzsbil.supabase.co','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InV3cHB2Y3hmbHJjc2lidXpzYmlsIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MDA5MzgxOTQsImV4cCI6MjAxNjUxNDE5NH0.CkxYSQH0uLfwx9GVUlO6AYMU2FMLAxGMrwEKvyPv7Oo')
@@ -47,6 +46,10 @@ export const useDataStore = defineStore('data', () => {
const bankStatements = ref([])
const historyItems = ref([])
const numberRanges = ref([])
const notifications = ref([])
const absenceRequests = ref([])
const accounts = ref([])
const taxTypes = ref([])
async function fetchData () {
fetchDocuments()
@@ -74,6 +77,10 @@ export const useDataStore = defineStore('data', () => {
await fetchBankStatements()
await fetchHistoryItems()
await fetchNumberRanges()
await fetchNotifications()
await fetchAbsenceRequests()
await fetchAccounts()
await fetchTaxTypes()
loaded.value = true
}
@@ -104,6 +111,10 @@ export const useDataStore = defineStore('data', () => {
bankStatements.value= []
historyItems.value = []
numberRanges.value = []
notifications.value = []
absenceRequests.value = []
accounts.value = []
taxTypes.value = []
}
async function fetchOwnTenant () {
@@ -117,7 +128,7 @@ export const useDataStore = defineStore('data', () => {
bankAccounts.value = (await supabase.from("bankAccounts").select()).data
}
async function fetchBankStatements () {
bankStatements.value = (await supabase.from("bankStatements").select()).data
bankStatements.value = (await supabase.from("bankStatements").select().order("date", {ascending:false})).data
}
async function fetchEvents () {
events.value = (await supabase.from("events").select()).data
@@ -176,6 +187,18 @@ export const useDataStore = defineStore('data', () => {
async function fetchNumberRanges () {
numberRanges.value = (await supabase.from("numberRanges").select()).data
}
async function fetchNotifications () {
notifications.value = (await supabase.from("notifications").select().order("created_at", {ascending: false})).data
}
async function fetchAbsenceRequests () {
absenceRequests.value = (await supabase.from("absenceRequests").select()).data
}
async function fetchAccounts () {
accounts.value = (await supabase.from("accounts").select()).data
}
async function fetchTaxTypes () {
taxTypes.value = (await supabase.from("taxTypes").select()).data
}
async function fetchDocuments () {
documents.value = (await supabase.from("documents").select()).data
@@ -224,6 +247,10 @@ export const useDataStore = defineStore('data', () => {
return contacts.value.filter(item => item.customer === customerId)
})
const getContactsByVendorId = computed(() => (vendorId:string) => {
return contacts.value.filter(item => item.vendor === vendorId)
})
const getDocumentsByProjectId = computed(() => (projectId:string) => {
return documents.value.filter(item => item.project === projectId)
})
@@ -232,10 +259,36 @@ export const useDataStore = defineStore('data', () => {
return times.value.filter(time => time.projectId === projectId)
})
const getTasksByProjectId = computed(() => (projectId:string) => {
return tasks.value.filter(item => item.project === projectId)
})
const getHistoryItemsByCustomer = computed(() => (customerId:string) => {
return historyItems.value.filter(item => item.customer === customerId)
})
const getHistoryItemsByVendor = computed(() => (vendorId:string) => {
return historyItems.value.filter(item => item.vendor === vendorId)
})
const getMovementsBySpaceId = computed(() => (spaceId:string) => {
return movements.value.filter(movement => movement.spaceId === spaceId)
})
const getStockByProductId = computed(() => (productId:string) => {
let productMovements = movements.value.filter(movement => movement.productId === productId)
let count = 0
productMovements.forEach(movement => {
count += movement.quantity
})
return count
})
const getEventTypes = computed(() => {
return ownTenant.value.calendarConfig.eventTypes
})
@@ -279,6 +332,16 @@ export const useDataStore = defineStore('data', () => {
backgroundColor: "black"
}
}),
...absenceRequests.value.map(absence => {
return {
resourceId: absence.user,
resourceType: "person",
title: absence.reason,
start: dayJs(absence.start).toDate(),
end: dayJs(absence.end).add(1,'day').toDate(),
allDay: true
}
})
]
})
@@ -304,7 +367,7 @@ export const useDataStore = defineStore('data', () => {
return contacts.value.find(item => item.id === itemId)
})
const getVehiclesById = computed(() => (itemId:string) => {
const getVehicleById = computed(() => (itemId:string) => {
return vehicles.value.find(item => item.id === itemId)
})
@@ -324,10 +387,18 @@ export const useDataStore = defineStore('data', () => {
return jobs.value.find(item => item.id === itemId)
})
const getAbsenceRequestById = computed(() => (itemId:string) => {
return absenceRequests.value.find(item => item.id === itemId)
})
const getProfileById = computed(() => (itemId:string) => {
return profiles.value.find(item => item.id === itemId)
})
const getAccountById = computed(() => (accountId:string) => {
return accounts.value.find(item => item.id === accountId)
})
const getProjectById = computed(() => (itemId:string) => {
let project = projects.value.find(project => project.id === itemId)
@@ -367,6 +438,10 @@ export const useDataStore = defineStore('data', () => {
bankStatements,
historyItems,
numberRanges,
notifications,
absenceRequests,
accounts,
taxTypes,
//Functions
fetchData,
clearStore,
@@ -393,15 +468,22 @@ export const useDataStore = defineStore('data', () => {
fetchVendors,
fetchVendorInvoices,
fetchNumberRanges,
fetchNotifications,
fetchDocuments,
fetchAbsenceRequests,
addHistoryItem,
//Getters
getOpenTasksCount,
getMovementsBySpace,
getContactsByCustomerId,
getContactsByVendorId,
getDocumentsByProjectId,
getTimesByProjectId,
getTasksByProjectId,
getMovementsBySpaceId,
getStockByProductId,
getHistoryItemsByCustomer,
getHistoryItemsByVendor,
getEventTypes,
getTimeTypes,
getDocumentTags,
@@ -412,13 +494,15 @@ export const useDataStore = defineStore('data', () => {
getVendorInvoiceById,
getContractById,
getContactById,
getVehiclesById,
getVehicleById,
getDocumentById,
getSpaceById,
getCustomerById,
getJobById,
getAbsenceRequestById,
getProjectById,
getProfileById
getProfileById,
getAccountById,
}