Merge branch 'devCorrected' into 'beta'
fixed archived filtering See merge request fedeo/software!9
This commit is contained in:
74
components/ArchiveButton.vue
Normal file
74
components/ArchiveButton.vue
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<script setup>
|
||||||
|
const emit = defineEmits(['confirmed'])
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
color: {
|
||||||
|
type: String,
|
||||||
|
required:false
|
||||||
|
},
|
||||||
|
variant: {
|
||||||
|
type: String,
|
||||||
|
required:false
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
required:false
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
const {color,variant, type} = props
|
||||||
|
|
||||||
|
const dataStore = useDataStore()
|
||||||
|
const dataType = dataStore.dataTypes[type]
|
||||||
|
|
||||||
|
const showModal = ref(false)
|
||||||
|
|
||||||
|
const emitConfirm = () => {
|
||||||
|
showModal.value = false
|
||||||
|
emit('confirmed')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UButton
|
||||||
|
:color="color"
|
||||||
|
:variant="variant"
|
||||||
|
@click="showModal = true"
|
||||||
|
>
|
||||||
|
Archivieren
|
||||||
|
</UButton>
|
||||||
|
<UModal v-model="showModal">
|
||||||
|
<UCard>
|
||||||
|
<template #header>
|
||||||
|
<span class="text-md font-bold">Archivieren bestätigen</span>
|
||||||
|
</template>
|
||||||
|
Möchten Sie diese/-s/-n {{dataType.labelSingle}} wirklich archivieren?
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<div class="text-right">
|
||||||
|
<UButtonGroup>
|
||||||
|
<UButton
|
||||||
|
variant="outline"
|
||||||
|
@click="showModal = false"
|
||||||
|
>
|
||||||
|
Abbrechen
|
||||||
|
</UButton>
|
||||||
|
<UButton
|
||||||
|
@click="emitConfirm"
|
||||||
|
class="ml-2"
|
||||||
|
color="rose"
|
||||||
|
>
|
||||||
|
Archivieren
|
||||||
|
</UButton>
|
||||||
|
</UButtonGroup>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</UCard>
|
||||||
|
</UModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -171,7 +171,7 @@ const moveFile = async () => {
|
|||||||
|
|
||||||
</template>
|
</template>
|
||||||
<div class="flex flex-row">
|
<div class="flex flex-row">
|
||||||
<div class="w-1/3">
|
<div :class="useCapacitor().getIsNative() ? ['w-full'] : ['w-1/3']">
|
||||||
<object
|
<object
|
||||||
class="bigPreview"
|
class="bigPreview"
|
||||||
:data="`${props.documentData.url}#toolbar=0&navpanes=0&scrollbar=0`"
|
:data="`${props.documentData.url}#toolbar=0&navpanes=0&scrollbar=0`"
|
||||||
@@ -186,21 +186,14 @@ const moveFile = async () => {
|
|||||||
v-else
|
v-else
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-2/3 p-5">
|
<div class="w-2/3 p-5" v-if="!useCapacitor().getIsNative()">
|
||||||
<UButtonGroup>
|
<UButtonGroup>
|
||||||
<ButtonWithConfirm
|
<ArchiveButton
|
||||||
color="rose"
|
color="rose"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
type="files"
|
||||||
@confirmed="archiveDocument"
|
@confirmed="archiveDocument"
|
||||||
>
|
/>
|
||||||
<template #button>
|
|
||||||
Archivieren
|
|
||||||
</template>
|
|
||||||
<template #header>
|
|
||||||
<span class="text-md text-black font-bold">Archivieren bestätigen</span>
|
|
||||||
</template>
|
|
||||||
Möchten Sie die Datei wirklich archivieren?
|
|
||||||
</ButtonWithConfirm>
|
|
||||||
|
|
||||||
<UButton
|
<UButton
|
||||||
:to="props.documentData.url"
|
:to="props.documentData.url"
|
||||||
|
|||||||
@@ -254,20 +254,13 @@ const updateItem = async () => {
|
|||||||
>{{item.id ? `${dataType.labelSingle} bearbeiten` : `${dataType.labelSingle} erstellen` }}</h1>
|
>{{item.id ? `${dataType.labelSingle} bearbeiten` : `${dataType.labelSingle} erstellen` }}</h1>
|
||||||
</template>
|
</template>
|
||||||
<template #right>
|
<template #right>
|
||||||
<ButtonWithConfirm
|
<ArchiveButton
|
||||||
v-if="platform !== 'mobile'"
|
|
||||||
color="rose"
|
color="rose"
|
||||||
|
v-if="platform !== 'mobile'"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@confirmed="dataStore.updateItem(type,{...item,archived: true}, oldItem)"
|
:type="type"
|
||||||
>
|
@confirmed="useEntities(type).archive(item.id)"
|
||||||
<template #button>
|
/>
|
||||||
Archivieren
|
|
||||||
</template>
|
|
||||||
<template #header>
|
|
||||||
<span class="text-md text-black dark:text-white font-bold">Archivieren bestätigen</span>
|
|
||||||
</template>
|
|
||||||
Möchten Sie das {{dataType.labelSingle}} {{item[dataType.templateColumns.find(i => i.title).key]}} wirklich archivieren?
|
|
||||||
</ButtonWithConfirm>
|
|
||||||
<UButton
|
<UButton
|
||||||
v-if="item.id"
|
v-if="item.id"
|
||||||
@click="updateItem"
|
@click="updateItem"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import {useTempStore} from "~/stores/temp.js";
|
import {useTempStore} from "~/stores/temp.js";
|
||||||
import FloatingActionButton from "~/components/mobile/FloatingActionButton.vue";
|
import FloatingActionButton from "~/components/mobile/FloatingActionButton.vue";
|
||||||
import EntityTable from "~/components/EntityTable.vue";
|
import EntityTable from "~/components/EntityTable.vue";
|
||||||
import EntityListMobile from "~/components/EntityListMobile.vue";
|
import EntityTableMobile from "~/components/EntityTableMobile.vue";
|
||||||
|
|
||||||
const { has } = usePermission()
|
const { has } = usePermission()
|
||||||
|
|
||||||
@@ -145,6 +145,7 @@ const filteredRows = computed(() => {
|
|||||||
</template>
|
</template>
|
||||||
</UDashboardNavbar>
|
</UDashboardNavbar>
|
||||||
|
|
||||||
|
|
||||||
<UDashboardToolbar>
|
<UDashboardToolbar>
|
||||||
<template #left v-if="$slots['left-toolbar']">
|
<template #left v-if="$slots['left-toolbar']">
|
||||||
<slot name="left-toolbar"/>
|
<slot name="left-toolbar"/>
|
||||||
@@ -181,7 +182,7 @@ const filteredRows = computed(() => {
|
|||||||
</USelectMenu>
|
</USelectMenu>
|
||||||
</template>
|
</template>
|
||||||
</UDashboardToolbar>
|
</UDashboardToolbar>
|
||||||
<EntityListMobile
|
<EntityTableMobile
|
||||||
v-if="platform === 'mobile'"
|
v-if="platform === 'mobile'"
|
||||||
:type="props.type"
|
:type="props.type"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
|
|||||||
@@ -220,7 +220,6 @@ const changePinned = async () => {
|
|||||||
</UButton>
|
</UButton>
|
||||||
</template>
|
</template>
|
||||||
</UDashboardNavbar>
|
</UDashboardNavbar>
|
||||||
|
|
||||||
<UTabs
|
<UTabs
|
||||||
:items="dataType.showTabs"
|
:items="dataType.showTabs"
|
||||||
v-if="props.item.id && platform !== 'mobile'"
|
v-if="props.item.id && platform !== 'mobile'"
|
||||||
@@ -326,7 +325,7 @@ const changePinned = async () => {
|
|||||||
@updateNeeded="emit('updateNeeded')"
|
@updateNeeded="emit('updateNeeded')"
|
||||||
:platform="platform"
|
:platform="platform"
|
||||||
/>
|
/>
|
||||||
<EntityShowSubPhases
|
<!--<EntityShowSubPhases
|
||||||
:item="props.item"
|
:item="props.item"
|
||||||
:top-level-type="type"
|
:top-level-type="type"
|
||||||
v-else-if="sub.label === 'Phasen'"
|
v-else-if="sub.label === 'Phasen'"
|
||||||
@@ -355,7 +354,7 @@ const changePinned = async () => {
|
|||||||
:top-level-type="type"
|
:top-level-type="type"
|
||||||
v-else
|
v-else
|
||||||
:platform="platform"
|
:platform="platform"
|
||||||
/>
|
/>-->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</UDashboardPanelContent>
|
</UDashboardPanelContent>
|
||||||
|
|||||||
190
components/PDFViewer.vue
Normal file
190
components/PDFViewer.vue
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref, onMounted, watch } from "vue"
|
||||||
|
import { VPdfViewer } from "@vue-pdf-viewer/viewer"
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
// Beispiel: "FEDEO/26/filesbyid/11990345-8711-4e23-8851-c50f028fc915/RE25-1081.pdf"
|
||||||
|
fileId: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
scale: {
|
||||||
|
type: Number,
|
||||||
|
default: 1.2,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const pdfSrc = ref(null) // ObjectURL fürs Viewer
|
||||||
|
const { $api } = useNuxtApp()
|
||||||
|
|
||||||
|
async function loadPdf(id) {
|
||||||
|
try {
|
||||||
|
const arrayBuffer = await $api(`/api/files/download/${id}`, {
|
||||||
|
method: "POST",
|
||||||
|
responseType: "arrayBuffer", // wichtig für pdf.js
|
||||||
|
})
|
||||||
|
const blob = new Blob([arrayBuffer], { type: "application/pdf" })
|
||||||
|
pdfSrc.value = URL.createObjectURL(blob)
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Fehler beim Laden der PDF:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => loadPdf(props.fileId))
|
||||||
|
watch(() => props.fileId, (newPath) => {
|
||||||
|
if (newPath) loadPdf(newPath)
|
||||||
|
})
|
||||||
|
|
||||||
|
const vpvRef = ref(null);
|
||||||
|
|
||||||
|
//Zoom Control
|
||||||
|
const zoomControl = computed(() => vpvRef.value?.zoomControl)
|
||||||
|
|
||||||
|
const currentScale = computed(() => {
|
||||||
|
return zoomControl.value?.scale
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleZoomTool = (type) => {
|
||||||
|
const zoomCtrl = unref(zoomControl)
|
||||||
|
if (!zoomCtrl) return
|
||||||
|
|
||||||
|
const scale = unref(currentScale)
|
||||||
|
if (type === "in") {
|
||||||
|
scale && zoomCtrl.zoom(scale + 0.25)
|
||||||
|
} else if (type === "out") {
|
||||||
|
scale && zoomCtrl.zoom(scale - 0.25)
|
||||||
|
} else {
|
||||||
|
zoomCtrl.zoom(type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Page Control
|
||||||
|
const pageControl = computed(() => vpvRef.value?.pageControl)
|
||||||
|
const currentPageInput = computed(() => pageControl.value?.currentPage)
|
||||||
|
const searchControl = computed(() => vpvRef.value?.searchControl)
|
||||||
|
const totalMatches = computed(() => searchControl.value?.searchMatches?.totalMatches)
|
||||||
|
|
||||||
|
const isNextPageButtonDisable = computed(() =>
|
||||||
|
pageControl.value?.currentPage === pageControl.value?.totalPages
|
||||||
|
)
|
||||||
|
|
||||||
|
const isPreviousPageButtonDisable = computed(() =>
|
||||||
|
pageControl.value?.currentPage === 1
|
||||||
|
)
|
||||||
|
|
||||||
|
const prevPage = () => {
|
||||||
|
const isFirstPage = pageControl.value?.currentPage === 1
|
||||||
|
if (isFirstPage) return
|
||||||
|
pageControl.value?.goToPage(pageControl.value?.currentPage - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextPage = () => {
|
||||||
|
const isLastPage = pageControl.value?.currentPage === pageControl.value?.totalPages
|
||||||
|
if (isLastPage) return
|
||||||
|
pageControl.value?.goToPage(pageControl.value?.currentPage + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleKeyPress = (event) => {
|
||||||
|
if (event.key === "Enter") {
|
||||||
|
handlePageInput(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Handle Download
|
||||||
|
const downloadControl = computed(() => vpvRef.value?.downloadControl)
|
||||||
|
|
||||||
|
const handleDownloadFile = () => {
|
||||||
|
const downloadCtrl = unref(downloadControl)
|
||||||
|
if (!downloadCtrl) return
|
||||||
|
downloadCtrl.download()
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(downloadControl, (downloadCtrl) => {
|
||||||
|
if (!downloadCtrl) return
|
||||||
|
|
||||||
|
downloadCtrl.onError = (error) => {
|
||||||
|
console.log("Download error", error)
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadCtrl.onComplete = () => {
|
||||||
|
console.log("Download completed")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-col gap-4 justify-self-center">
|
||||||
|
<div class="flex items-center gap-4 text-[#7862FF] bg-pale-blue border-[#D7D1FB] rounded-lg p-2 justify-center">
|
||||||
|
|
||||||
|
<!-- Zoom out button -->
|
||||||
|
<UButton
|
||||||
|
@click="() => handleZoomTool('pageWidth')"
|
||||||
|
icon="i-heroicons-document-text"
|
||||||
|
variant="outline"
|
||||||
|
></UButton>
|
||||||
|
|
||||||
|
<UButton
|
||||||
|
@click="() => handleZoomTool('out')"
|
||||||
|
icon="i-heroicons-magnifying-glass-minus"
|
||||||
|
variant="outline"
|
||||||
|
></UButton>
|
||||||
|
|
||||||
|
<!-- Zoom in button -->
|
||||||
|
<UButton
|
||||||
|
@click="() => handleZoomTool('in')"
|
||||||
|
icon="i-heroicons-magnifying-glass-plus"
|
||||||
|
variant="outline"
|
||||||
|
></UButton>
|
||||||
|
<UButton
|
||||||
|
@click="prevPage"
|
||||||
|
:disabled="isPreviousPageButtonDisable"
|
||||||
|
icon="i-heroicons-chevron-up"
|
||||||
|
variant="outline"
|
||||||
|
></UButton>
|
||||||
|
|
||||||
|
<UButton
|
||||||
|
@click="handleDownloadFile"
|
||||||
|
variant="outline"
|
||||||
|
icon="i-heroicons-arrow-down-on-square"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Page number input and total pages display -->
|
||||||
|
<div class="flex items-center text-sm font-normal">
|
||||||
|
<UInput
|
||||||
|
v-model="currentPageInput"
|
||||||
|
class="w-24 h-8 rounded-sm focus:outline-none"
|
||||||
|
@change="handleKeyPress"
|
||||||
|
>
|
||||||
|
<template #trailing>
|
||||||
|
/ {{ pageControl?.totalPages }}
|
||||||
|
</template>
|
||||||
|
</UInput>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Next page button -->
|
||||||
|
<UButton
|
||||||
|
@click="nextPage"
|
||||||
|
:disabled="isNextPageButtonDisable"
|
||||||
|
icon="i-heroicons-chevron-down"
|
||||||
|
variant="outline"
|
||||||
|
></UButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pdf-container">
|
||||||
|
<VPdfViewer
|
||||||
|
v-if="pdfSrc"
|
||||||
|
:src="pdfSrc"
|
||||||
|
style="height: 78vh; width: 100%;"
|
||||||
|
:toolbar-options="false"
|
||||||
|
ref="vpvRef"
|
||||||
|
/>
|
||||||
|
<div v-else>Lade PDF…</div>
|
||||||
|
</div></template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.pdf-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,23 +1,14 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
|
||||||
const supabase = useSupabaseClient()
|
const auth = useAuthStore()
|
||||||
const router = useRouter()
|
|
||||||
const profileStore = useProfileStore()
|
|
||||||
|
|
||||||
const tenant = ref({})
|
|
||||||
|
|
||||||
const setupPage = async () => {
|
|
||||||
tenant.value = (await supabase.from("tenants").select().eq("id",profileStore.currentTenant).single()).data
|
|
||||||
}
|
|
||||||
|
|
||||||
setupPage()
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<h1 class="font-bold text-xl">Willkommen zurück {{profileStore.activeProfile.fullName}}</h1>
|
<h1 class="font-bold text-xl">Willkommen zurück {{auth.profile.full_name}}</h1>
|
||||||
<span v-if="tenant.id">bei {{tenant.name}}</span>
|
<span v-if="auth.activeTenant">bei {{auth.activeTenantData.name}}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -17,10 +17,14 @@ export const useCapacitor = () => {
|
|||||||
return deviceInfo.model.toLowerCase().includes('iphone')
|
return deviceInfo.model.toLowerCase().includes('iphone')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getIsNative = () => {
|
||||||
|
return Capacitor.isNativePlatform()
|
||||||
|
}
|
||||||
|
|
||||||
const getNetworkStatus = async () => {
|
const getNetworkStatus = async () => {
|
||||||
return await Network.getStatus()
|
return await Network.getStatus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return {getPlatform, getDeviceInfo, getNetworkStatus, getIsPhone}
|
return {getPlatform, getDeviceInfo, getNetworkStatus, getIsPhone, getIsNative}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
export const useError = (resourceType) => {
|
export const useErrorLogging = (resourceType) => {
|
||||||
const supabase = useSupabaseClient()
|
const supabase = useSupabaseClient()
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
const profileStore = useProfileStore()
|
const profileStore = useProfileStore()
|
||||||
@@ -6,7 +6,6 @@ export const useSum = () => {
|
|||||||
const getIncomingInvoiceSum = (invoice) => {
|
const getIncomingInvoiceSum = (invoice) => {
|
||||||
let sum = 0
|
let sum = 0
|
||||||
invoice.accounts.forEach(account => {
|
invoice.accounts.forEach(account => {
|
||||||
console.log(account)
|
|
||||||
|
|
||||||
|
|
||||||
sum += account.amountTax
|
sum += account.amountTax
|
||||||
@@ -23,21 +22,9 @@ export const useSum = () => {
|
|||||||
let total19 = 0
|
let total19 = 0
|
||||||
let total7 = 0
|
let total7 = 0
|
||||||
|
|
||||||
/*let usedadvanceinvoices = []
|
|
||||||
if(createddocument.usedAdvanceInvoices.length > 0) {
|
|
||||||
console.log(createddocument)
|
|
||||||
console.log(createddocument.usedAdvanceInvoices)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
console.log((await supabase.from("createddocuments").select().in("id", createddocument.usedAdvanceInvoices)))
|
|
||||||
|
|
||||||
usedadvanceinvoices = (await supabase.from("createddocuments").select().in("id", createddocument.usedAdvanceInvoices)).data
|
|
||||||
console.log(usedadvanceinvoices)
|
|
||||||
|
|
||||||
}*/
|
|
||||||
|
|
||||||
|
|
||||||
createddocument.rows.forEach(row => {
|
createddocument.rows.forEach(row => {
|
||||||
if(!['pagebreak','title','text'].includes(row.mode)){
|
if(!['pagebreak','title','text'].includes(row.mode)){
|
||||||
let rowPrice = Number(Number(row.quantity) * Number(row.price) * (1 - Number(row.discountPercent) /100) ).toFixed(3)
|
let rowPrice = Number(Number(row.quantity) * Number(row.price) * (1 - Number(row.discountPercent) /100) ).toFixed(3)
|
||||||
|
|||||||
@@ -485,7 +485,7 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_ENTITLEMENTS = App/App.entitlements;
|
CODE_SIGN_ENTITLEMENTS = App/App.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 2;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_TEAM = GMCGQ8KK2P;
|
DEVELOPMENT_TEAM = GMCGQ8KK2P;
|
||||||
INFOPLIST_FILE = App/Info.plist;
|
INFOPLIST_FILE = App/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
|
||||||
@@ -493,7 +493,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.8;
|
MARKETING_VERSION = 2.0;
|
||||||
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
|
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = software.federspiel.fedeo;
|
PRODUCT_BUNDLE_IDENTIFIER = software.federspiel.fedeo;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
@@ -510,7 +510,7 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_ENTITLEMENTS = App/App.entitlements;
|
CODE_SIGN_ENTITLEMENTS = App/App.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 2;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_TEAM = GMCGQ8KK2P;
|
DEVELOPMENT_TEAM = GMCGQ8KK2P;
|
||||||
INFOPLIST_FILE = App/Info.plist;
|
INFOPLIST_FILE = App/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
|
||||||
@@ -518,7 +518,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.8;
|
MARKETING_VERSION = 2.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = software.federspiel.fedeo;
|
PRODUCT_BUNDLE_IDENTIFIER = software.federspiel.fedeo;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ def capacitor_pods
|
|||||||
pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios'
|
pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios'
|
||||||
pod 'CapacitorDevice', :path => '../../node_modules/@capacitor/device'
|
pod 'CapacitorDevice', :path => '../../node_modules/@capacitor/device'
|
||||||
pod 'CapacitorNetwork', :path => '../../node_modules/@capacitor/network'
|
pod 'CapacitorNetwork', :path => '../../node_modules/@capacitor/network'
|
||||||
|
pod 'CapacitorPreferences', :path => '../../node_modules/@capacitor/preferences'
|
||||||
pod 'CapacitorPluginSafeArea', :path => '../../node_modules/capacitor-plugin-safe-area'
|
pod 'CapacitorPluginSafeArea', :path => '../../node_modules/capacitor-plugin-safe-area'
|
||||||
pod 'CordovaPluginsStatic', :path => '../capacitor-cordova-ios-plugins'
|
pod 'CordovaPluginsStatic', :path => '../capacitor-cordova-ios-plugins'
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ PODS:
|
|||||||
- Capacitor
|
- Capacitor
|
||||||
- CapacitorPluginSafeArea (4.0.0):
|
- CapacitorPluginSafeArea (4.0.0):
|
||||||
- Capacitor
|
- Capacitor
|
||||||
|
- CapacitorPreferences (6.0.3):
|
||||||
|
- Capacitor
|
||||||
- CordovaPluginsStatic (7.1.0):
|
- CordovaPluginsStatic (7.1.0):
|
||||||
- CapacitorCordova
|
- CapacitorCordova
|
||||||
- OneSignalXCFramework (= 5.2.10)
|
- OneSignalXCFramework (= 5.2.10)
|
||||||
@@ -64,6 +66,7 @@ DEPENDENCIES:
|
|||||||
- "CapacitorDevice (from `../../node_modules/@capacitor/device`)"
|
- "CapacitorDevice (from `../../node_modules/@capacitor/device`)"
|
||||||
- "CapacitorNetwork (from `../../node_modules/@capacitor/network`)"
|
- "CapacitorNetwork (from `../../node_modules/@capacitor/network`)"
|
||||||
- CapacitorPluginSafeArea (from `../../node_modules/capacitor-plugin-safe-area`)
|
- CapacitorPluginSafeArea (from `../../node_modules/capacitor-plugin-safe-area`)
|
||||||
|
- "CapacitorPreferences (from `../../node_modules/@capacitor/preferences`)"
|
||||||
- CordovaPluginsStatic (from `../capacitor-cordova-ios-plugins`)
|
- CordovaPluginsStatic (from `../capacitor-cordova-ios-plugins`)
|
||||||
- OneSignalXCFramework (< 6.0, >= 5.0)
|
- OneSignalXCFramework (< 6.0, >= 5.0)
|
||||||
|
|
||||||
@@ -82,6 +85,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: "../../node_modules/@capacitor/network"
|
:path: "../../node_modules/@capacitor/network"
|
||||||
CapacitorPluginSafeArea:
|
CapacitorPluginSafeArea:
|
||||||
:path: "../../node_modules/capacitor-plugin-safe-area"
|
:path: "../../node_modules/capacitor-plugin-safe-area"
|
||||||
|
CapacitorPreferences:
|
||||||
|
:path: "../../node_modules/@capacitor/preferences"
|
||||||
CordovaPluginsStatic:
|
CordovaPluginsStatic:
|
||||||
:path: "../capacitor-cordova-ios-plugins"
|
:path: "../capacitor-cordova-ios-plugins"
|
||||||
|
|
||||||
@@ -91,9 +96,10 @@ SPEC CHECKSUMS:
|
|||||||
CapacitorDevice: 069faf433b3a99c3d5f0e500fbe634f60a8c6a84
|
CapacitorDevice: 069faf433b3a99c3d5f0e500fbe634f60a8c6a84
|
||||||
CapacitorNetwork: 30c2e78a0ed32530656cb426c8ee6c2caec10dbf
|
CapacitorNetwork: 30c2e78a0ed32530656cb426c8ee6c2caec10dbf
|
||||||
CapacitorPluginSafeArea: 22031c3436269ca80fac90ec2c94bc7c1e59a81d
|
CapacitorPluginSafeArea: 22031c3436269ca80fac90ec2c94bc7c1e59a81d
|
||||||
|
CapacitorPreferences: f3eadae2369ac3ab8e21743a2959145b0d1286a3
|
||||||
CordovaPluginsStatic: f722d4ff434f50099581e690d579b7c108f490e6
|
CordovaPluginsStatic: f722d4ff434f50099581e690d579b7c108f490e6
|
||||||
OneSignalXCFramework: 1a3b28dfbff23aabce585796d23c1bef37772774
|
OneSignalXCFramework: 1a3b28dfbff23aabce585796d23c1bef37772774
|
||||||
|
|
||||||
PODFILE CHECKSUM: ccfbce7f13cfefd953204fe26b280d6431731aa5
|
PODFILE CHECKSUM: d76fcd3d35c3f8c3708303de70ef45a76cc6e2b5
|
||||||
|
|
||||||
COCOAPODS: 1.16.2
|
COCOAPODS: 1.16.2
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ const route = useRoute()
|
|||||||
|
|
||||||
const auth = useAuthStore()
|
const auth = useAuthStore()
|
||||||
|
|
||||||
//profileStore.initializeData((await supabase.auth.getUser()).data.user.id)
|
|
||||||
|
|
||||||
const month = dayjs().format("MM")
|
const month = dayjs().format("MM")
|
||||||
|
|
||||||
|
|||||||
@@ -13,8 +13,9 @@ const { isHelpSlideoverOpen } = useDashboard()
|
|||||||
const supabase = useSupabaseClient()
|
const supabase = useSupabaseClient()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
const auth = useAuthStore()
|
||||||
|
|
||||||
profileStore.initializeData((await supabase.auth.getUser()).data.user.id)
|
//profileStore.initializeData((await supabase.auth.getUser()).data.user.id)
|
||||||
|
|
||||||
const month = dayjs().format("MM")
|
const month = dayjs().format("MM")
|
||||||
|
|
||||||
@@ -64,40 +65,6 @@ const actions = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
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}`}})
|
|
||||||
},{
|
|
||||||
key: "tasks",
|
|
||||||
label: "Aufgaben",
|
|
||||||
commands: dataStore.tasks.map(item => { return {id: item.id, label: item.name, to: `/tasks/show/${item.id}`}})
|
|
||||||
},{
|
|
||||||
key: "plants",
|
|
||||||
label: "Objekte",
|
|
||||||
commands: dataStore.plants.map(item => { return {id: item.id, label: item.name, to: `/plants/show/${item.id}`}})
|
|
||||||
},{
|
|
||||||
key: "projects",
|
|
||||||
label: "Projekte",
|
|
||||||
commands: dataStore.projects.map(item => { return {id: item.id, label: item.name, to: `/projects/show/${item.id}`}})
|
|
||||||
}
|
|
||||||
].filter(Boolean))
|
|
||||||
const footerLinks = [/*{
|
const footerLinks = [/*{
|
||||||
label: 'Invite people',
|
label: 'Invite people',
|
||||||
icon: 'i-heroicons-plus',
|
icon: 'i-heroicons-plus',
|
||||||
@@ -111,87 +78,163 @@ const footerLinks = [/*{
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UDashboardLayout class="safearea" v-if="profileStore.loaded">
|
<div v-if="!auth.loading">
|
||||||
|
<div v-if="auth.activeTenantData?.locked === 'maintenance_tenant'">
|
||||||
<UDashboardPanel :width="250" :resizable="{ min: 200, max: 300 }" collapsible>
|
<UContainer class="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-900">
|
||||||
<UDashboardNavbar style="margin-top: env(safe-area-inset-top, 10px) !important;" :class="['!border-transparent']" :ui="{ left: 'flex-1' }">
|
<UCard class="max-w-lg text-center p-10">
|
||||||
<template #left>
|
<UColorModeImage
|
||||||
<ProfileDropdown class="w-full" />
|
light="/Logo_Hell_Weihnachten.png"
|
||||||
</template>
|
dark="/Logo_Dunkel_Weihnachten.png"
|
||||||
</UDashboardNavbar>
|
class=" mx-auto my-10"
|
||||||
|
v-if="month === '12'"
|
||||||
<UDashboardSidebar id="sidebar">
|
/>
|
||||||
<template #header>
|
<UColorModeImage
|
||||||
<UDashboardSearchButton v-if="!useCapacitor().getIsPhone()" label="Suche..."/>
|
light="/Logo.png"
|
||||||
</template>
|
dark="/Logo_Dark.png"
|
||||||
|
class="mx-auto my-10"
|
||||||
<MainNav/>
|
v-else
|
||||||
|
/>
|
||||||
<div class="flex-1" />
|
<div class="flex justify-center mb-6">
|
||||||
|
<UIcon name="i-heroicons-exclamation-triangle-solid" class="w-16 h-16 text-yellow-500" />
|
||||||
|
|
||||||
<template #footer>
|
|
||||||
<div class="flex flex-col w-full">
|
|
||||||
|
|
||||||
<UDashboardSidebarLinks :links="footerLinks" />
|
|
||||||
<UDivider class="sticky bottom-0" />
|
|
||||||
<UserDropdown style="margin-bottom: env(safe-area-inset-bottom, 10px) !important;"/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</template>
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-white mb-4">
|
||||||
</UDashboardSidebar>
|
Wartungsarbeiten
|
||||||
</UDashboardPanel>
|
</h1>
|
||||||
|
<p class="text-gray-600 dark:text-gray-300 mb-8">
|
||||||
|
Dieser FEDEO Mandant wird derzeit gewartet. Bitte versuche es in einigen Minuten erneut oder verwende einen anderen Mandanten.
|
||||||
|
</p>
|
||||||
|
<div class="mx-auto text-left flex flex-row justify-between my-3" v-for="tenant in auth.tenants">
|
||||||
|
{{tenant.name}}
|
||||||
|
<UButton
|
||||||
|
:disabled="tenant.locked"
|
||||||
|
@click="auth.switchTenant(tenant.id)"
|
||||||
|
>Wählen</UButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
<UDashboardPage style="height: 90vh">
|
|
||||||
<UDashboardPanel grow>
|
|
||||||
<slot />
|
|
||||||
</UDashboardPanel>
|
|
||||||
</UDashboardPage>
|
|
||||||
|
|
||||||
<div class="mobileFooter bg-white dark:bg-gray-950">
|
</UCard>
|
||||||
<UButton
|
</UContainer>
|
||||||
icon="i-heroicons-home"
|
|
||||||
to="/mobile/"
|
|
||||||
variant="ghost"
|
|
||||||
:color="route.fullPath === '/mobile' ? 'primary' : 'gray'"
|
|
||||||
/>
|
|
||||||
<UButton
|
|
||||||
icon="i-heroicons-clipboard-document-check"
|
|
||||||
to="/standardEntity/tasks"
|
|
||||||
variant="ghost"
|
|
||||||
:color="route.fullPath === '/standardEntity/tasks' ? 'primary' : 'gray'"
|
|
||||||
/>
|
|
||||||
<UButton
|
|
||||||
icon="i-heroicons-rectangle-stack"
|
|
||||||
to="/standardEntity/projects"
|
|
||||||
variant="ghost"
|
|
||||||
:color="route.fullPath === '/standardEntity/projects' ? 'primary' : 'gray'"
|
|
||||||
/>
|
|
||||||
<!-- <UButton
|
|
||||||
icon="i-heroicons-clock"
|
|
||||||
to="/workingtimes"
|
|
||||||
variant="ghost"
|
|
||||||
:color="route.fullPath === '/workingtimes' ? 'primary' : 'gray'"
|
|
||||||
/>-->
|
|
||||||
<UButton
|
|
||||||
icon="i-heroicons-bars-4"
|
|
||||||
to="/mobile/menu"
|
|
||||||
variant="ghost"
|
|
||||||
:color="route.fullPath === '/mobile/menu' ? 'primary' : 'gray'"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else-if="auth.activeTenantData?.locked === 'maintenance'">
|
||||||
|
<UContainer class="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-900">
|
||||||
|
<UCard class="max-w-lg text-center p-10">
|
||||||
|
<UColorModeImage
|
||||||
|
light="/Logo_Hell_Weihnachten.png"
|
||||||
|
dark="/Logo_Dunkel_Weihnachten.png"
|
||||||
|
class=" mx-auto my-10"
|
||||||
|
v-if="month === '12'"
|
||||||
|
/>
|
||||||
|
<UColorModeImage
|
||||||
|
light="/Logo.png"
|
||||||
|
dark="/Logo_Dark.png"
|
||||||
|
class="mx-auto my-10"
|
||||||
|
v-else
|
||||||
|
/>
|
||||||
|
<div class="flex justify-center mb-6">
|
||||||
|
<UIcon name="i-heroicons-exclamation-triangle-solid" class="w-16 h-16 text-yellow-500" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-white mb-4">
|
||||||
|
Wartungsarbeiten
|
||||||
|
</h1>
|
||||||
|
<p class="text-gray-600 dark:text-gray-300 mb-8">
|
||||||
|
FEDEO wird derzeit gewartet. Bitte versuche es in einigen Minuten erneut.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
</UCard>
|
||||||
|
</UContainer>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="auth.activeTenantData?.locked === 'no_subscription'">
|
||||||
|
<UContainer class="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-900">
|
||||||
|
<UCard class="max-w-lg text-center p-10">
|
||||||
|
<UColorModeImage
|
||||||
|
light="/Logo_Hell_Weihnachten.png"
|
||||||
|
dark="/Logo_Dunkel_Weihnachten.png"
|
||||||
|
class=" mx-auto my-10"
|
||||||
|
v-if="month === '12'"
|
||||||
|
/>
|
||||||
|
<UColorModeImage
|
||||||
|
light="/Logo.png"
|
||||||
|
dark="/Logo_Dark.png"
|
||||||
|
class="mx-auto my-10"
|
||||||
|
v-else
|
||||||
|
/>
|
||||||
|
<div class="flex justify-center mb-6">
|
||||||
|
<UIcon name="i-heroicons-credit-card" class="w-16 h-16 text-red-600" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-white mb-4">
|
||||||
|
Kein Aktives Abonnement für diesen Mandant.
|
||||||
|
</h1>
|
||||||
|
<p class="text-gray-600 dark:text-gray-300 mb-8">
|
||||||
|
Bitte wenden Sie sich an den FEDEO Support um ein Abonnement zu erhalten oder verwenden Sie einen anderen Mandanten.
|
||||||
|
</p>
|
||||||
|
<div class="mx-auto text-left flex flex-row justify-between my-3" v-for="tenant in auth.tenants">
|
||||||
|
{{tenant.name}}
|
||||||
|
<UButton
|
||||||
|
:disabled="tenant.locked"
|
||||||
|
@click="auth.switchTenant(tenant.id)"
|
||||||
|
>Wählen</UButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</UCard>
|
||||||
|
</UContainer>
|
||||||
|
</div>
|
||||||
|
<UDashboardLayout class="safearea" v-else>
|
||||||
|
|
||||||
|
<UDashboardPage style="height: 90vh">
|
||||||
|
<UDashboardPanel grow>
|
||||||
|
<slot />
|
||||||
|
</UDashboardPanel>
|
||||||
|
</UDashboardPage>
|
||||||
|
|
||||||
|
<div class="mobileFooter bg-white dark:bg-gray-950">
|
||||||
|
<UButton
|
||||||
|
icon="i-heroicons-home"
|
||||||
|
to="/mobile/"
|
||||||
|
variant="ghost"
|
||||||
|
:color="route.fullPath === '/mobile' ? 'primary' : 'gray'"
|
||||||
|
/>
|
||||||
|
<UButton
|
||||||
|
icon="i-heroicons-clipboard-document-check"
|
||||||
|
to="/standardEntity/tasks"
|
||||||
|
variant="ghost"
|
||||||
|
:color="route.fullPath === '/standardEntity/tasks' ? 'primary' : 'gray'"
|
||||||
|
/>
|
||||||
|
<UButton
|
||||||
|
icon="i-heroicons-rectangle-stack"
|
||||||
|
to="/standardEntity/projects"
|
||||||
|
variant="ghost"
|
||||||
|
:color="route.fullPath === '/standardEntity/projects' ? 'primary' : 'gray'"
|
||||||
|
/>
|
||||||
|
<!-- <UButton
|
||||||
|
icon="i-heroicons-clock"
|
||||||
|
to="/workingtimes"
|
||||||
|
variant="ghost"
|
||||||
|
:color="route.fullPath === '/workingtimes' ? 'primary' : 'gray'"
|
||||||
|
/>-->
|
||||||
|
<UButton
|
||||||
|
icon="i-heroicons-bars-4"
|
||||||
|
to="/mobile/menu"
|
||||||
|
variant="ghost"
|
||||||
|
:color="route.fullPath === '/mobile/menu' ? 'primary' : 'gray'"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- ~/components/HelpSlideover.vue -->
|
<!-- ~/components/HelpSlideover.vue -->
|
||||||
<HelpSlideover/>
|
<HelpSlideover/>
|
||||||
<!-- ~/components/NotificationsSlideover.vue -->
|
<!-- ~/components/NotificationsSlideover.vue -->
|
||||||
<NotificationsSlideover />
|
<NotificationsSlideover />
|
||||||
|
|
||||||
|
|
||||||
|
</UDashboardLayout>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ClientOnly>
|
|
||||||
<LazyUDashboardSearch :groups="groups" hide-color-mode/>
|
|
||||||
</ClientOnly>
|
|
||||||
</UDashboardLayout>
|
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
class="flex flex-col"
|
class="flex flex-col"
|
||||||
@@ -208,15 +251,26 @@ const footerLinks = [/*{
|
|||||||
class="w-1/3 mx-auto my-10"
|
class="w-1/3 mx-auto my-10"
|
||||||
v-else
|
v-else
|
||||||
/>
|
/>
|
||||||
<div v-if="dataStore.showProfileSelection">
|
<div v-if="!auth.activeTenant" class="w-full mx-auto text-center">
|
||||||
<ProfileSelection/>
|
<!-- Tenant Selection -->
|
||||||
|
<h3 class="text-center font-bold text-2xl mb-5">Kein Aktiver Mandant. Bitte wählen Sie ein Mandant.</h3>
|
||||||
|
<div class="mx-auto w-5/6 flex flex-row justify-between my-3" v-for="tenant in auth.tenants">
|
||||||
|
<span class="text-left">{{tenant.name}}</span>
|
||||||
|
<UButton
|
||||||
|
@click="auth.switchTenant(tenant.id)"
|
||||||
|
>Wählen</UButton>
|
||||||
|
</div>
|
||||||
|
<UButton
|
||||||
|
variant="outline"
|
||||||
|
color="rose"
|
||||||
|
@click="auth.logout()"
|
||||||
|
>Abmelden</UButton>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
|
|
||||||
|
|
||||||
<UProgress animation="carousel" class="w-3/4 mx-auto mt-10" />
|
<UProgress animation="carousel" class="w-3/4 mx-auto mt-10" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
9
middleware/redirectToMobileIndex.ts
Normal file
9
middleware/redirectToMobileIndex.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
export default defineNuxtRouteMiddleware(async (to, _from) => {
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
console.log(await useCapacitor().getIsPhone())
|
||||||
|
|
||||||
|
if(await useCapacitor().getIsPhone() && _from.path !== '/mobile') {
|
||||||
|
return router.push('/mobile')
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -25,7 +25,7 @@ export default defineNuxtConfig({
|
|||||||
transpile: ['@vuepic/vue-datepicker']
|
transpile: ['@vuepic/vue-datepicker']
|
||||||
},
|
},
|
||||||
|
|
||||||
modules: ['@pinia/nuxt', '@nuxt/ui', '@nuxt/content', '@nuxtjs/supabase', "nuxt-editorjs", '@nuxtjs/fontaine', 'nuxt-viewport', 'nuxt-tiptap-editor', '@nuxtjs/leaflet'],
|
modules: ['@pinia/nuxt', '@nuxt/ui', '@nuxtjs/supabase', "nuxt-editorjs", '@nuxtjs/fontaine', 'nuxt-viewport', 'nuxt-tiptap-editor', '@nuxtjs/leaflet'],
|
||||||
|
|
||||||
routeRules: {
|
routeRules: {
|
||||||
'/printing': {ssr: false}
|
'/printing': {ssr: false}
|
||||||
|
|||||||
0
package-lock.json
generated
0
package-lock.json
generated
14
package.json
14
package.json
@@ -10,21 +10,21 @@
|
|||||||
"postinstall": "nuxt prepare"
|
"postinstall": "nuxt prepare"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@capacitor/cli": "^7.0.0",
|
||||||
"@nuxtjs/leaflet": "^1.2.3",
|
"@nuxtjs/leaflet": "^1.2.3",
|
||||||
"@nuxtjs/supabase": "^1.1.4",
|
"@nuxtjs/supabase": "^1.1.4",
|
||||||
"nuxt": "^3.14.1592",
|
"nuxt": "^3.14.1592",
|
||||||
"nuxt-tiptap-editor": "^1.2.0",
|
"nuxt-tiptap-editor": "^1.2.0",
|
||||||
"vite-plugin-pwa": "^0.17.3",
|
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
"vue-router": "^4.2.5"
|
"vue-router": "^4.2.5"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@capacitor/android": "^7.1.0",
|
"@capacitor/android": "^7.0.0",
|
||||||
"@capacitor/cli": "^7.1.0",
|
"@capacitor/core": "^7.0.0",
|
||||||
"@capacitor/core": "^7.1.0",
|
|
||||||
"@capacitor/device": "^7.0.0",
|
"@capacitor/device": "^7.0.0",
|
||||||
"@capacitor/ios": "^7.1.0",
|
"@capacitor/ios": "^7.0.0",
|
||||||
"@capacitor/network": "^7.0.0",
|
"@capacitor/network": "^7.0.0",
|
||||||
|
"@capacitor/preferences": "^7.0.0",
|
||||||
"@fullcalendar/core": "^6.1.10",
|
"@fullcalendar/core": "^6.1.10",
|
||||||
"@fullcalendar/daygrid": "^6.1.10",
|
"@fullcalendar/daygrid": "^6.1.10",
|
||||||
"@fullcalendar/interaction": "^6.1.10",
|
"@fullcalendar/interaction": "^6.1.10",
|
||||||
@@ -34,7 +34,6 @@
|
|||||||
"@fullcalendar/timegrid": "^6.1.10",
|
"@fullcalendar/timegrid": "^6.1.10",
|
||||||
"@fullcalendar/vue3": "^6.1.10",
|
"@fullcalendar/vue3": "^6.1.10",
|
||||||
"@iconify/json": "^2.2.171",
|
"@iconify/json": "^2.2.171",
|
||||||
"@nuxt/content": "^2.9.0",
|
|
||||||
"@nuxt/ui-pro": "^1.6.0",
|
"@nuxt/ui-pro": "^1.6.0",
|
||||||
"@nuxtjs/fontaine": "^0.4.1",
|
"@nuxtjs/fontaine": "^0.4.1",
|
||||||
"@nuxtjs/google-fonts": "^3.1.0",
|
"@nuxtjs/google-fonts": "^3.1.0",
|
||||||
@@ -49,6 +48,7 @@
|
|||||||
"@tiptap/vue-3": "^2.1.15",
|
"@tiptap/vue-3": "^2.1.15",
|
||||||
"@vicons/ionicons5": "^0.12.0",
|
"@vicons/ionicons5": "^0.12.0",
|
||||||
"@vue-leaflet/vue-leaflet": "^0.10.1",
|
"@vue-leaflet/vue-leaflet": "^0.10.1",
|
||||||
|
"@vue-pdf-viewer/viewer": "^3.0.1",
|
||||||
"@vuepic/vue-datepicker": "^7.4.0",
|
"@vuepic/vue-datepicker": "^7.4.0",
|
||||||
"@zip.js/zip.js": "^2.7.32",
|
"@zip.js/zip.js": "^2.7.32",
|
||||||
"array-sort": "^1.0.0",
|
"array-sort": "^1.0.0",
|
||||||
@@ -81,4 +81,4 @@
|
|||||||
"vuetify": "^3.4.0-beta.1",
|
"vuetify": "^3.4.0-beta.1",
|
||||||
"zebra-browser-print-wrapper": "^0.1.4"
|
"zebra-browser-print-wrapper": "^0.1.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,6 @@ const profileStore = useProfileStore()
|
|||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const mode = ref(route.params.mode || "show")
|
const mode = ref(route.params.mode || "show")
|
||||||
const supabase = useSupabaseClient()
|
|
||||||
|
|
||||||
const itemInfo = ref({statementallocations:[]})
|
const itemInfo = ref({statementallocations:[]})
|
||||||
const oldItemInfo = ref({})
|
const oldItemInfo = ref({})
|
||||||
@@ -202,15 +201,7 @@ const archiveStatement = async () => {
|
|||||||
let temp = itemInfo.value
|
let temp = itemInfo.value
|
||||||
delete temp.statementallocations
|
delete temp.statementallocations
|
||||||
|
|
||||||
await dataStore.updateItem("bankstatements", {...temp,archived:true})
|
await useEntities("bankstatements").archive(temp.id)
|
||||||
|
|
||||||
|
|
||||||
const {data,error} = await supabase.from("historyitems").insert({
|
|
||||||
createdBy: useProfileStore().activeProfile.id,
|
|
||||||
tenant: useProfileStore().currentTenant,
|
|
||||||
text: "Bankbuchung archiviert",
|
|
||||||
bankStatement: itemInfo.value.id
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@@ -254,19 +245,12 @@ const archiveStatement = async () => {
|
|||||||
</UBadge>
|
</UBadge>
|
||||||
</template>
|
</template>
|
||||||
<template #right>
|
<template #right>
|
||||||
<ButtonWithConfirm
|
<ArchiveButton
|
||||||
color="rose"
|
color="rose"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
type="bankstatements"
|
||||||
@confirmed="archiveStatement"
|
@confirmed="archiveStatement"
|
||||||
>
|
/>
|
||||||
<template #button>
|
|
||||||
Archivieren
|
|
||||||
</template>
|
|
||||||
<template #header>
|
|
||||||
<span class="text-md text-black font-bold">Archivieren bestätigen</span>
|
|
||||||
</template>
|
|
||||||
Möchten Sie die Kontobewegung wirklich archivieren?
|
|
||||||
</ButtonWithConfirm>
|
|
||||||
</template>
|
</template>
|
||||||
</UDashboardNavbar>
|
</UDashboardNavbar>
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ const dataStore = useDataStore()
|
|||||||
const profileStore = useProfileStore()
|
const profileStore = useProfileStore()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const supabase = useSupabaseClient()
|
|
||||||
const modal = useModal()
|
const modal = useModal()
|
||||||
const auth = useAuthStore()
|
const auth = useAuthStore()
|
||||||
|
|
||||||
@@ -134,7 +133,8 @@ const setupPage = async () => {
|
|||||||
console.log(route.query.loadMode)
|
console.log(route.query.loadMode)
|
||||||
|
|
||||||
if (route.query.loadMode === "deliveryNotes") {
|
if (route.query.loadMode === "deliveryNotes") {
|
||||||
let linkedDocuments = (await supabase.from("createddocuments").select().in("id", JSON.parse(route.query.linkedDocuments))).data
|
let linkedDocuments = (await useEntities("createddocuments").select()).filter(i => JSON.parse(route.query.linkedDocuments).includes(i.id))
|
||||||
|
//let linkedDocuments = (await supabase.from("createddocuments").select().in("id", JSON.parse(route.query.linkedDocuments))).data
|
||||||
|
|
||||||
//TODO: Implement Checking for Same Customer, Contact and Project
|
//TODO: Implement Checking for Same Customer, Contact and Project
|
||||||
|
|
||||||
@@ -1523,21 +1523,14 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
|||||||
</UButton>
|
</UButton>
|
||||||
</template>
|
</template>
|
||||||
<template #right>
|
<template #right>
|
||||||
<ButtonWithConfirm
|
<ArchiveButton
|
||||||
v-if="itemInfo.state === 'Entwurf' || itemInfo.type === 'serialInvoices'"
|
|
||||||
color="rose"
|
color="rose"
|
||||||
|
type="createddocuments"
|
||||||
|
v-if="itemInfo.state === 'Entwurf' || itemInfo.type === 'serialInvoices'"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@confirmed="useEntities('createddocuments').update(itemInfo.id,{archived: true}),
|
@confirmed="useEntities('createddocuments').update(itemInfo.id,{archived: true}),
|
||||||
router.push('/')"
|
router.push('/')"
|
||||||
>
|
/>
|
||||||
<template #button>
|
|
||||||
Archivieren
|
|
||||||
</template>
|
|
||||||
<template #header>
|
|
||||||
<span class="text-md dark:text-whitetext-black font-bold">Archivieren bestätigen</span>
|
|
||||||
</template>
|
|
||||||
Möchten Sie diesen Ausgangsbeleg wirklich archivieren?
|
|
||||||
</ButtonWithConfirm>
|
|
||||||
<UButton
|
<UButton
|
||||||
icon="i-mdi-content-save"
|
icon="i-mdi-content-save"
|
||||||
@click="saveDocument('Entwurf',true)"
|
@click="saveDocument('Entwurf',true)"
|
||||||
|
|||||||
@@ -47,10 +47,11 @@
|
|||||||
</template>
|
</template>
|
||||||
</USelectMenu>
|
</USelectMenu>
|
||||||
<USelectMenu
|
<USelectMenu
|
||||||
|
v-if="selectableFilters.length > 0"
|
||||||
icon="i-heroicons-adjustments-horizontal-solid"
|
icon="i-heroicons-adjustments-horizontal-solid"
|
||||||
multiple
|
multiple
|
||||||
v-model="selectedFilters"
|
v-model="selectedFilters"
|
||||||
:options="['Nur offene anzeigen']"
|
:options="selectableFilters"
|
||||||
:color="selectedFilters.length > 0 ? 'primary' : 'white'"
|
:color="selectedFilters.length > 0 ? 'primary' : 'white'"
|
||||||
:ui-menu="{ width: 'min-w-max' }"
|
:ui-menu="{ width: 'min-w-max' }"
|
||||||
>
|
>
|
||||||
@@ -61,6 +62,15 @@
|
|||||||
</template>
|
</template>
|
||||||
</UDashboardToolbar>
|
</UDashboardToolbar>
|
||||||
<UTabs :items="selectedTypes" class="m-3">
|
<UTabs :items="selectedTypes" class="m-3">
|
||||||
|
<template #default="{item}">
|
||||||
|
{{item.label}}
|
||||||
|
<UBadge
|
||||||
|
variant="outline"
|
||||||
|
class="ml-2"
|
||||||
|
>
|
||||||
|
{{filteredRows.filter(i => item.key === 'invoices' ? ['invoices','advanceInvoices','cancellationInvoices'].includes(i.type) : item.key === i.type).length}}
|
||||||
|
</UBadge>
|
||||||
|
</template>
|
||||||
<template #item="{item}">
|
<template #item="{item}">
|
||||||
<div style="height: 80vh; overflow-y: scroll">
|
<div style="height: 80vh; overflow-y: scroll">
|
||||||
<UTable
|
<UTable
|
||||||
@@ -182,12 +192,15 @@ const dataStore = useDataStore()
|
|||||||
const tempStore = useTempStore()
|
const tempStore = useTempStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
|
const type = "createddocuments"
|
||||||
|
const dataType = dataStore.dataTypes[type]
|
||||||
|
|
||||||
const items = ref([])
|
const items = ref([])
|
||||||
const selectedItem = ref(0)
|
const selectedItem = ref(0)
|
||||||
|
|
||||||
|
|
||||||
const setupPage = async () => {
|
const setupPage = async () => {
|
||||||
items.value = (await useEntities("createddocuments").select("*, customer(id,name), statementallocations(id,amount),linkedDocument(*)","documentNumber",true)).filter(i => !i.archived)
|
items.value = (await useEntities("createddocuments").select("*, customer(id,name), statementallocations(id,amount),linkedDocument(*)","documentNumber",true, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
setupPage()
|
setupPage()
|
||||||
@@ -279,24 +292,34 @@ const clearSearchString = () => {
|
|||||||
tempStore.clearSearchString('createddocuments')
|
tempStore.clearSearchString('createddocuments')
|
||||||
searchString.value = ''
|
searchString.value = ''
|
||||||
}
|
}
|
||||||
const selectedFilters = ref([])
|
const selectableFilters = ref(dataType.filters.map(i => i.name))
|
||||||
|
const selectedFilters = ref(dataType.filters.filter(i => i.default).map(i => i.name) || [])
|
||||||
|
|
||||||
const filteredRows = computed(() => {
|
const filteredRows = computed(() => {
|
||||||
|
|
||||||
let temp = items.value.filter(i => types.value.find(x => x.key === 'invoices' ? ['invoices', 'advanceInvoices', 'cancellationInvoices'].includes(i.type) : x.key === i.type))
|
let tempItems = items.value.filter(i => types.value.find(x => x.key === 'invoices' ? ['invoices', 'advanceInvoices', 'cancellationInvoices'].includes(i.type) : x.key === i.type))
|
||||||
temp = temp.filter(i => i.type !== "serialInvoices")
|
tempItems = tempItems.filter(i => i.type !== "serialInvoices")
|
||||||
|
|
||||||
/*if(showDrafts.value === true) {
|
tempItems = tempItems.map(i => {
|
||||||
temp = temp.filter(i => i.state === "Entwurf")
|
return {
|
||||||
} else {
|
...i,
|
||||||
temp = temp.filter(i => i.state !== "Entwurf")
|
class: i.archived ? 'bg-red-500/50 dark:bg-red-400/50' : null
|
||||||
}*/
|
}
|
||||||
|
})
|
||||||
|
|
||||||
if (selectedFilters.value.includes("Nur offene anzeigen")) {
|
if(selectedFilters.value.length > 0) {
|
||||||
temp = temp.filter(i => !isPaid(i))
|
selectedFilters.value.forEach(filterName => {
|
||||||
|
let filter = dataType.filters.find(i => i.name === filterName)
|
||||||
|
tempItems = tempItems.filter(filter.filterFunction)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return useSearch(searchString.value, temp.slice().reverse())
|
|
||||||
|
tempItems = useSearch(searchString.value, tempItems)
|
||||||
|
|
||||||
|
|
||||||
|
return useSearch(searchString.value, tempItems.slice().reverse())
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -13,12 +13,13 @@ const modal = useModal()
|
|||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const auth = useAuthStore()
|
const auth = useAuthStore()
|
||||||
|
const dataStore = useDataStore()
|
||||||
|
|
||||||
const itemInfo = ref({})
|
const itemInfo = ref({})
|
||||||
const linkedDocument =ref({})
|
const linkedDocument =ref({})
|
||||||
const setupPage = async () => {
|
const setupPage = async () => {
|
||||||
if(route.params) {
|
if(route.params) {
|
||||||
if(route.params.id) itemInfo.value = await useEntities("createddocuments").selectSingle(route.params.id,"*, files(*)")
|
if(route.params.id) itemInfo.value = await useEntities("createddocuments").selectSingle(route.params.id,"*, files(*), linkedDocument(*)")
|
||||||
|
|
||||||
console.log(itemInfo.value)
|
console.log(itemInfo.value)
|
||||||
|
|
||||||
@@ -95,14 +96,27 @@ const openEmail = () => {
|
|||||||
>
|
>
|
||||||
Kunde
|
Kunde
|
||||||
</UButton>
|
</UButton>
|
||||||
|
<UButton
|
||||||
|
v-if="itemInfo.linkedDocument"
|
||||||
|
@click="router.push(`/standardEntity/createDocument/show/${itemInfo.linkedDocument}`)"
|
||||||
|
icon="i-heroicons-link"
|
||||||
|
variant="outline"
|
||||||
|
>
|
||||||
|
{{dataStore.documentTypesForCreation[itemInfo.linkedDocument.type].labelSingle}} - {{itemInfo.linkedDocument.documentNumber}}
|
||||||
|
</UButton>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
</UDashboardToolbar>
|
</UDashboardToolbar>
|
||||||
<UDashboardPanelContent>
|
<UDashboardPanelContent>
|
||||||
<object
|
<!-- <object
|
||||||
:data="linkedDocument.url"
|
:data="linkedDocument.url"
|
||||||
class="w-full previewDocumentMobile"
|
class="w-full previewDocumentMobile"
|
||||||
/>
|
/>-->
|
||||||
|
<PDFViewer v-if="linkedDocument.id" :file-id="linkedDocument.id" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</UDashboardPanelContent>
|
</UDashboardPanelContent>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -170,19 +170,12 @@ const findIncomingInvoiceErrors = computed(() => {
|
|||||||
<template>
|
<template>
|
||||||
<UDashboardNavbar :title="'Eingangsbeleg erstellen'">
|
<UDashboardNavbar :title="'Eingangsbeleg erstellen'">
|
||||||
<template #right>
|
<template #right>
|
||||||
<ButtonWithConfirm
|
<ArchiveButton
|
||||||
color="rose"
|
color="rose"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
type="incominginvoices"
|
||||||
@confirmed="useEntities('incominginvoices').archive(route.params.id)"
|
@confirmed="useEntities('incominginvoices').archive(route.params.id)"
|
||||||
>
|
/>
|
||||||
<template #button>
|
|
||||||
Archivieren
|
|
||||||
</template>
|
|
||||||
<template #header>
|
|
||||||
<span class="text-md text-black font-bold">Archivieren bestätigen</span>
|
|
||||||
</template>
|
|
||||||
Möchten Sie den Eingangsbeleg wirklich archivieren?
|
|
||||||
</ButtonWithConfirm>
|
|
||||||
<UButton
|
<UButton
|
||||||
@click="updateIncomingInvoice(false)"
|
@click="updateIncomingInvoice(false)"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -47,69 +47,49 @@ const sort = ref({
|
|||||||
direction: 'desc'
|
direction: 'desc'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const type = "incominginvoices"
|
||||||
|
const dataType = dataStore.dataTypes[type]
|
||||||
|
|
||||||
const setupPage = async () => {
|
const setupPage = async () => {
|
||||||
//items.value = await useSupabaseSelect("incominginvoices","*, vendor(id,name), statementallocations(id,amount)","created_at",false)
|
//items.value = await useSupabaseSelect("incominginvoices","*, vendor(id,name), statementallocations(id,amount)","created_at",false)
|
||||||
items.value = await useEntities("incominginvoices").select("*, vendor(id,name), statementallocations(id,amount)",sort.value.column,sort.value.direction === "asc")
|
items.value = await useEntities(type).select("*, vendor(id,name), statementallocations(id,amount)",sort.value.column,sort.value.direction === "asc")
|
||||||
}
|
}
|
||||||
|
|
||||||
setupPage()
|
setupPage()
|
||||||
|
|
||||||
const templateColumns = [
|
const selectedColumns = ref(tempStore.columns[type] ? tempStore.columns[type] : dataType.templateColumns.filter(i => !i.disabledInTable))
|
||||||
{
|
const columns = computed(() => dataType.templateColumns.filter((column) => !column.disabledInTable && selectedColumns.value.find(i => i.key === column.key)))
|
||||||
key: 'reference',
|
|
||||||
label: "Referenz:",
|
|
||||||
sortable: true,
|
|
||||||
}, {
|
|
||||||
key: 'state',
|
|
||||||
label: "Status:"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "date",
|
|
||||||
label: "Datum",
|
|
||||||
sortable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "vendor",
|
|
||||||
label: "Lieferant",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "amount",
|
|
||||||
label: "Betrag",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "dueDate",
|
|
||||||
label: "Fälligkeitsdatum",
|
|
||||||
sortable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "paid",
|
|
||||||
label: "Bezahlt"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "paymentType",
|
|
||||||
label: "Zahlart",
|
|
||||||
sortable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "description",
|
|
||||||
label: "Beschreibung"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
const selectedColumns = ref(templateColumns)
|
|
||||||
const columns = computed(() => templateColumns.filter((column) => selectedColumns.value.includes(column)))
|
|
||||||
|
|
||||||
|
const selectableFilters = ref(dataType.filters.map(i => i.name))
|
||||||
|
const selectedFilters = ref(dataType.filters.filter(i => i.default).map(i => i.name) || [])
|
||||||
|
|
||||||
|
const searchString = ref(tempStore.searchStrings[type] ||'')
|
||||||
const searchString = ref(tempStore.searchStrings['incominginvoices'] ||'')
|
|
||||||
|
|
||||||
const clearSearchString = () => {
|
const clearSearchString = () => {
|
||||||
tempStore.clearSearchString('incominginvoices')
|
tempStore.clearSearchString(type)
|
||||||
searchString.value = ''
|
searchString.value = ''
|
||||||
}
|
}
|
||||||
const filteredRows = computed(() => {
|
const filteredRows = computed(() => {
|
||||||
let filteredItems = useSearch(searchString.value, items.value)
|
let tempItems = items.value.map(i => {
|
||||||
|
return {
|
||||||
|
...i,
|
||||||
|
class: i.archived ? 'bg-red-500/50 dark:bg-red-400/50' : null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
return [...filteredItems.filter(i => i.state === "Vorbereitet"), ...filteredItems.filter(i => i.state !== "Vorbereitet")]
|
if(selectedFilters.value.length > 0) {
|
||||||
|
selectedFilters.value.forEach(filterName => {
|
||||||
|
let filter = dataType.filters.find(i => i.name === filterName)
|
||||||
|
tempItems = tempItems.filter(filter.filterFunction)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
tempItems = useSearch(searchString.value, tempItems)
|
||||||
|
|
||||||
|
|
||||||
|
return [...tempItems.filter(i => i.state === "Vorbereitet"), ...tempItems.filter(i => i.state !== "Vorbereitet")]
|
||||||
|
|
||||||
|
|
||||||
})
|
})
|
||||||
@@ -133,7 +113,7 @@ const isPaid = (item) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const selectIncomingInvoice = (invoice) => {
|
const selectIncomingInvoice = (invoice) => {
|
||||||
if(invoice.state === "Vorbereitet") {
|
if(invoice.state === "Vorbereitet" ) {
|
||||||
router.push(`/incomingInvoices/edit/${invoice.id}`)
|
router.push(`/incomingInvoices/edit/${invoice.id}`)
|
||||||
} else {
|
} else {
|
||||||
router.push(`/incomingInvoices/show/${invoice.id}`)
|
router.push(`/incomingInvoices/show/${invoice.id}`)
|
||||||
@@ -157,7 +137,7 @@ const selectIncomingInvoice = (invoice) => {
|
|||||||
placeholder="Suche..."
|
placeholder="Suche..."
|
||||||
class="hidden lg:block"
|
class="hidden lg:block"
|
||||||
@keydown.esc="$event.target.blur()"
|
@keydown.esc="$event.target.blur()"
|
||||||
@change="tempStore.modifySearchString('incominginvoices',searchString)"
|
@change="tempStore.modifySearchString(type,searchString)"
|
||||||
>
|
>
|
||||||
<template #trailing>
|
<template #trailing>
|
||||||
<UKbd value="/" />
|
<UKbd value="/" />
|
||||||
@@ -179,57 +159,90 @@ const selectIncomingInvoice = (invoice) => {
|
|||||||
<USelectMenu
|
<USelectMenu
|
||||||
v-model="selectedColumns"
|
v-model="selectedColumns"
|
||||||
icon="i-heroicons-adjustments-horizontal-solid"
|
icon="i-heroicons-adjustments-horizontal-solid"
|
||||||
:options="templateColumns"
|
:options="dataType.templateColumns.filter(i => !i.disabledInTable)"
|
||||||
multiple
|
multiple
|
||||||
class="hidden lg:block"
|
class="hidden lg:block"
|
||||||
by="key"
|
by="key"
|
||||||
|
:color="selectedColumns.length !== dataType.templateColumns.filter(i => !i.disabledInTable).length ? 'primary' : 'white'"
|
||||||
|
:ui-menu="{ width: 'min-w-max' }"
|
||||||
|
@change="tempStore.modifyColumns(type,selectedColumns)"
|
||||||
>
|
>
|
||||||
<template #label>
|
<template #label>
|
||||||
Spalten
|
Spalten
|
||||||
</template>
|
</template>
|
||||||
</USelectMenu>
|
</USelectMenu>
|
||||||
|
<USelectMenu
|
||||||
|
v-if="selectableFilters.length > 0"
|
||||||
|
icon="i-heroicons-adjustments-horizontal-solid"
|
||||||
|
multiple
|
||||||
|
v-model="selectedFilters"
|
||||||
|
:options="selectableFilters"
|
||||||
|
:color="selectedFilters.length > 0 ? 'primary' : 'white'"
|
||||||
|
:ui-menu="{ width: 'min-w-max' }"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
Filter
|
||||||
|
</template>
|
||||||
|
</USelectMenu>
|
||||||
</template>
|
</template>
|
||||||
</UDashboardToolbar>
|
</UDashboardToolbar>
|
||||||
|
|
||||||
<UDashboardPanelContent>
|
<UTabs
|
||||||
<UTable
|
class="m-3"
|
||||||
v-model:sort="sort"
|
:items="[{label: 'In Bearbeitung'},{label: 'Gebucht'}]"
|
||||||
sort-mode="manual"
|
>
|
||||||
@update:sort="setupPage"
|
<template #default="{item}">
|
||||||
:rows="filteredRows"
|
{{item.label}}
|
||||||
:columns="columns"
|
<UBadge
|
||||||
class="w-full"
|
variant="outline"
|
||||||
:ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }"
|
class="ml-2"
|
||||||
@select="(i) => selectIncomingInvoice(i) "
|
>
|
||||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Belege anzuzeigen' }"
|
{{filteredRows.filter(i => item.label === 'Gebucht' ? i.state === 'Gebucht' : i.state !== 'Gebucht' ).length}}
|
||||||
>
|
</UBadge>
|
||||||
<template #reference-data="{row}">
|
</template>
|
||||||
<span v-if="row === filteredRows[selectedItem]" class="text-primary-500 font-bold">{{row.reference}}</span>
|
<template #item="{item}">
|
||||||
<span v-else>{{row.reference}}</span>
|
<div style="height: 80dvh; overflow-y: scroll">
|
||||||
</template>
|
<UTable
|
||||||
<template #state-data="{row}">
|
v-model:sort="sort"
|
||||||
<span v-if="row.state === 'Vorbereitet'" class="text-cyan-500">{{row.state}}</span>
|
sort-mode="manual"
|
||||||
<span v-else-if="row.state === 'Entwurf'" class="text-red-500">{{row.state}}</span>
|
@update:sort="setupPage"
|
||||||
<span v-else-if="row.state === 'Gebucht'" class="text-primary-500">{{row.state}}</span>
|
:rows="filteredRows.filter(i => item.label === 'Gebucht' ? i.state === 'Gebucht' : i.state !== 'Gebucht' )"
|
||||||
</template>
|
:columns="columns"
|
||||||
<template #date-data="{row}">
|
class="w-full"
|
||||||
{{dayjs(row.date).format("DD.MM.YYYY")}}
|
:ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }"
|
||||||
</template>
|
@select="(i) => selectIncomingInvoice(i) "
|
||||||
<template #vendor-data="{row}">
|
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Belege anzuzeigen' }"
|
||||||
{{row.vendor ? row.vendor.name : ""}}
|
>
|
||||||
</template>
|
<template #reference-data="{row}">
|
||||||
<template #amount-data="{row}">
|
<span v-if="row === filteredRows[selectedItem]" class="text-primary-500 font-bold">{{row.reference}}</span>
|
||||||
{{displayCurrency(sum.getIncomingInvoiceSum(row))}}
|
<span v-else>{{row.reference}}</span>
|
||||||
</template>
|
</template>
|
||||||
<template #dueDate-data="{row}">
|
<template #state-data="{row}">
|
||||||
<span v-if="row.dueDate">{{dayjs(row.dueDate).format("DD.MM.YYYY")}}</span>
|
<span v-if="row.state === 'Vorbereitet'" class="text-cyan-500">{{row.state}}</span>
|
||||||
</template>
|
<span v-else-if="row.state === 'Entwurf'" class="text-red-500">{{row.state}}</span>
|
||||||
<template #paid-data="{row}">
|
<span v-else-if="row.state === 'Gebucht'" class="text-primary-500">{{row.state}}</span>
|
||||||
<span v-if="isPaid(row)" class="text-primary-500">Bezahlt</span>
|
</template>
|
||||||
<span v-else class="text-rose-600">Offen</span>
|
<template #date-data="{row}">
|
||||||
</template>
|
{{dayjs(row.date).format("DD.MM.YYYY")}}
|
||||||
</UTable>
|
</template>
|
||||||
</UDashboardPanelContent>
|
<template #vendor-data="{row}">
|
||||||
|
{{row.vendor ? row.vendor.name : ""}}
|
||||||
|
</template>
|
||||||
|
<template #amount-data="{row}">
|
||||||
|
{{displayCurrency(sum.getIncomingInvoiceSum(row))}}
|
||||||
|
</template>
|
||||||
|
<template #dueDate-data="{row}">
|
||||||
|
<span v-if="row.dueDate">{{dayjs(row.dueDate).format("DD.MM.YYYY")}}</span>
|
||||||
|
</template>
|
||||||
|
<template #paid-data="{row}">
|
||||||
|
<span v-if="isPaid(row)" class="text-primary-500">Bezahlt</span>
|
||||||
|
<span v-else class="text-rose-600">Offen</span>
|
||||||
|
</template>
|
||||||
|
</UTable>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
</UTabs>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,11 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
|
||||||
|
definePageMeta({
|
||||||
|
middleware: 'redirect-to-mobile-index'
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
import DisplayPresentProfiles from "~/components/noAutoLoad/displayPresentProfiles.vue";
|
import DisplayPresentProfiles from "~/components/noAutoLoad/displayPresentProfiles.vue";
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,11 @@ const doLogin = async (data:any) => {
|
|||||||
await auth.login(data.email, data.password)
|
await auth.login(data.email, data.password)
|
||||||
// Weiterleiten nach erfolgreichem Login
|
// Weiterleiten nach erfolgreichem Login
|
||||||
toast.add({title:"Einloggen erfolgreich"})
|
toast.add({title:"Einloggen erfolgreich"})
|
||||||
return navigateTo("/")
|
if(useCapacitor().getIsNative()) {
|
||||||
|
return navigateTo("/mobile")
|
||||||
|
} else {
|
||||||
|
return navigateTo("/")
|
||||||
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
toast.add({title:"Zugangsdaten falsch. Bitte überprüfen Sie Ihre Eingaben",color:"rose"})
|
toast.add({title:"Zugangsdaten falsch. Bitte überprüfen Sie Ihre Eingaben",color:"rose"})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ definePageMeta({
|
|||||||
layout: 'mobile'
|
layout: 'mobile'
|
||||||
})
|
})
|
||||||
|
|
||||||
const profileStore = useProfileStore()
|
//const profileStore = useProfileStore()
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ const profileStore = useProfileStore()
|
|||||||
>
|
>
|
||||||
<display-open-tasks/>
|
<display-open-tasks/>
|
||||||
</UDashboardCard>
|
</UDashboardCard>
|
||||||
<UDashboardCard
|
<!--<UDashboardCard
|
||||||
title="Anwesenheit"
|
title="Anwesenheit"
|
||||||
>
|
>
|
||||||
<display-running-working-time/>
|
<display-running-working-time/>
|
||||||
@@ -36,7 +36,7 @@ const profileStore = useProfileStore()
|
|||||||
v-if="profileStore.ownTenant.features.accounting"
|
v-if="profileStore.ownTenant.features.accounting"
|
||||||
>
|
>
|
||||||
<display-open-balances/>
|
<display-open-balances/>
|
||||||
</UDashboardCard>
|
</UDashboardCard>-->
|
||||||
<UDashboardCard
|
<UDashboardCard
|
||||||
title="Projekte"
|
title="Projekte"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ definePageMeta({
|
|||||||
layout: 'mobile',
|
layout: 'mobile',
|
||||||
})
|
})
|
||||||
|
|
||||||
const profileStore = useProfileStore()
|
const auth = useAuthStore()
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -65,17 +65,23 @@ const profileStore = useProfileStore()
|
|||||||
>
|
>
|
||||||
Objekte
|
Objekte
|
||||||
</UButton>
|
</UButton>
|
||||||
|
<UButton
|
||||||
|
class="w-full my-1"
|
||||||
|
@click="auth.logout()"
|
||||||
|
color="rose"
|
||||||
|
variant="outline"
|
||||||
|
>
|
||||||
|
Abmelden
|
||||||
|
</UButton>
|
||||||
|
|
||||||
<UDivider class="my-5">Unternehmen wechseln</UDivider>
|
<UDivider class="my-5">Unternehmen wechseln</UDivider>
|
||||||
|
|
||||||
<UButton
|
<div class="w-full flex flex-row justify-between my-3" v-for="tenant in auth.tenants">
|
||||||
v-for="option in profileStore.ownProfiles"
|
<span class="text-left">{{tenant.name}}</span>
|
||||||
class="my-1"
|
<UButton
|
||||||
variant="outline"
|
@click="auth.switchTenant(tenant.id)"
|
||||||
@click="profileStore.changeProfile(option.id)"
|
>Wechseln</UButton>
|
||||||
>
|
</div>
|
||||||
{{profileStore.tenants.find(i => i.id === option.tenant).name}}
|
|
||||||
</UButton>
|
|
||||||
|
|
||||||
|
|
||||||
</UDashboardPanelContent>
|
</UDashboardPanelContent>
|
||||||
|
|||||||
@@ -2,9 +2,7 @@
|
|||||||
import {setPageLayout} from "#app";
|
import {setPageLayout} from "#app";
|
||||||
import {useCapacitor} from "~/composables/useCapacitor.js";
|
import {useCapacitor} from "~/composables/useCapacitor.js";
|
||||||
|
|
||||||
definePageMeta({
|
|
||||||
layout: "default",
|
|
||||||
})
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const dataStore = useDataStore()
|
const dataStore = useDataStore()
|
||||||
const api = useNuxtApp().$api
|
const api = useNuxtApp().$api
|
||||||
@@ -12,7 +10,6 @@ const api = useNuxtApp().$api
|
|||||||
|
|
||||||
const type = route.params.type
|
const type = route.params.type
|
||||||
const platform = await useCapacitor().getIsPhone() ? "mobile" : "default"
|
const platform = await useCapacitor().getIsPhone() ? "mobile" : "default"
|
||||||
console.log(platform)
|
|
||||||
|
|
||||||
|
|
||||||
const dataType = dataStore.dataTypes[route.params.type]
|
const dataType = dataStore.dataTypes[route.params.type]
|
||||||
@@ -25,10 +22,9 @@ const item = ref({})
|
|||||||
|
|
||||||
const setupPage = async (sort_column = null, sort_direction = null) => {
|
const setupPage = async (sort_column = null, sort_direction = null) => {
|
||||||
loaded.value = false
|
loaded.value = false
|
||||||
|
setPageLayout(platform)
|
||||||
|
|
||||||
|
|
||||||
if (await useCapacitor().getIsPhone()) {
|
|
||||||
setPageLayout("mobile")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (route.params.mode) mode.value = route.params.mode
|
if (route.params.mode) mode.value = route.params.mode
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,19 @@
|
|||||||
|
import {Preferences} from "@capacitor/preferences";
|
||||||
|
|
||||||
export default defineNuxtPlugin(() => {
|
export default defineNuxtPlugin(() => {
|
||||||
const api = $fetch.create({
|
const api = $fetch.create({
|
||||||
baseURL: /*"http://localhost:3100"*/ "https://backend.fedeo.io",
|
baseURL: /*"http://192.168.1.227:3100"*/ "https://backend.fedeo.io",
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
onRequest({ options }) {
|
async onRequest({options}) {
|
||||||
// Token aus Cookie holen
|
// Token aus Cookie holen
|
||||||
let token = useCookie("token").value
|
let token: string | null | undefined = ""
|
||||||
|
if (await useCapacitor().getIsNative()) {
|
||||||
|
const {value} = await Preferences.get({key: 'token'});
|
||||||
|
token = value
|
||||||
|
} else {
|
||||||
|
token = useCookie("token").value
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Falls im Request explizit ein anderer JWT übergeben wird → diesen verwenden
|
// Falls im Request explizit ein anderer JWT übergeben wird → diesen verwenden
|
||||||
if (options.context && (options.context as any).jwt) {
|
if (options.context && (options.context as any).jwt) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { defineStore } from "pinia"
|
import { defineStore } from "pinia"
|
||||||
import router from "#app/plugins/router";
|
import router from "#app/plugins/router";
|
||||||
|
import {Preferences} from "@capacitor/preferences";
|
||||||
|
|
||||||
export const useAuthStore = defineStore("auth", {
|
export const useAuthStore = defineStore("auth", {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
@@ -15,7 +16,12 @@ export const useAuthStore = defineStore("auth", {
|
|||||||
actions: {
|
actions: {
|
||||||
async init(token) {
|
async init(token) {
|
||||||
await this.fetchMe(token)
|
await this.fetchMe(token)
|
||||||
navigateTo("/")
|
|
||||||
|
if(useCapacitor().getIsNative()) {
|
||||||
|
navigateTo("/mobile")
|
||||||
|
} else {
|
||||||
|
navigateTo("/")
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async login(email: string, password: string) {
|
async login(email: string, password: string) {
|
||||||
@@ -23,7 +29,15 @@ export const useAuthStore = defineStore("auth", {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
body: { email, password }
|
body: { email, password }
|
||||||
})
|
})
|
||||||
useCookie("token").value = token // persistieren
|
if(useCapacitor().getIsNative()) {
|
||||||
|
await Preferences.set({
|
||||||
|
key:"token",
|
||||||
|
value: token,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
useCookie("token").value = token // persistieren
|
||||||
|
|
||||||
|
}
|
||||||
await this.fetchMe(token)
|
await this.fetchMe(token)
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -38,7 +52,13 @@ export const useAuthStore = defineStore("auth", {
|
|||||||
this.profile = null
|
this.profile = null
|
||||||
this.activeTenant = null
|
this.activeTenant = null
|
||||||
this.tenants = []
|
this.tenants = []
|
||||||
useCookie("token").value = null
|
if(useCapacitor().getIsNative()) {
|
||||||
|
await Preferences.remove({ key: 'token' });
|
||||||
|
useCookie("token").value = null
|
||||||
|
} else {
|
||||||
|
useCookie("token").value = null
|
||||||
|
}
|
||||||
|
|
||||||
navigateTo("/login")
|
navigateTo("/login")
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -62,7 +82,7 @@ export const useAuthStore = defineStore("auth", {
|
|||||||
|
|
||||||
this.profile = me.profile
|
this.profile = me.profile
|
||||||
|
|
||||||
if(me.activeTenant) {
|
if(me.activeTenant > 0) {
|
||||||
this.activeTenant = me.activeTenant
|
this.activeTenant = me.activeTenant
|
||||||
this.activeTenantData = me.tenants.find(i => i.id === me.activeTenant)
|
this.activeTenantData = me.tenants.find(i => i.id === me.activeTenant)
|
||||||
this.loading = false
|
this.loading = false
|
||||||
@@ -79,13 +99,29 @@ export const useAuthStore = defineStore("auth", {
|
|||||||
|
|
||||||
async switchTenant(tenant_id: string) {
|
async switchTenant(tenant_id: string) {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
const { token } = await useNuxtApp().$api("/api/tenant/switch", {
|
const res = await useNuxtApp().$api("/api/tenant/switch", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: { tenant_id }
|
body: { tenant_id }
|
||||||
})
|
})
|
||||||
useCookie("token").value = token
|
console.log(res)
|
||||||
|
|
||||||
|
const {token} = res
|
||||||
|
|
||||||
|
if(await useCapacitor().getIsNative()) {
|
||||||
|
await Preferences.set({
|
||||||
|
key:"token",
|
||||||
|
value: token,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
useCookie("token").value = token // persistieren
|
||||||
|
|
||||||
|
}
|
||||||
await this.init(token)
|
await this.init(token)
|
||||||
navigateTo("/")
|
if(useCapacitor().getIsNative()) {
|
||||||
|
navigateTo("/mobile")
|
||||||
|
} else {
|
||||||
|
navigateTo("/")
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
hasPermission(key: string) {
|
hasPermission(key: string) {
|
||||||
|
|||||||
@@ -1552,6 +1552,19 @@ export const useDataStore = defineStore('data', () => {
|
|||||||
label: "Dokumente",
|
label: "Dokumente",
|
||||||
labelSingle: "Dokument",
|
labelSingle: "Dokument",
|
||||||
supabaseSelectWithInformation: "*, files(*), statementallocations(*)",
|
supabaseSelectWithInformation: "*, files(*), statementallocations(*)",
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
name: "Archivierte ausblenden",
|
||||||
|
default: true,
|
||||||
|
"filterFunction": function (row) {
|
||||||
|
if(!row.archived) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
files: {
|
files: {
|
||||||
isArchivable: true,
|
isArchivable: true,
|
||||||
@@ -1568,7 +1581,61 @@ export const useDataStore = defineStore('data', () => {
|
|||||||
incominginvoices: {
|
incominginvoices: {
|
||||||
label: "Eingangsrechnungen",
|
label: "Eingangsrechnungen",
|
||||||
labelSingle: "Eingangsrechnung",
|
labelSingle: "Eingangsrechnung",
|
||||||
redirect:true
|
redirect:true,
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
name: "Archivierte ausblenden",
|
||||||
|
default: true,
|
||||||
|
"filterFunction": function (row) {
|
||||||
|
if(!row.archived) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
templateColumns: [
|
||||||
|
{
|
||||||
|
key: 'reference',
|
||||||
|
label: "Referenz:",
|
||||||
|
sortable: true,
|
||||||
|
}, {
|
||||||
|
key: 'state',
|
||||||
|
label: "Status:"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "date",
|
||||||
|
label: "Datum",
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "vendor",
|
||||||
|
label: "Lieferant",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "amount",
|
||||||
|
label: "Betrag",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "dueDate",
|
||||||
|
label: "Fälligkeitsdatum",
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "paid",
|
||||||
|
label: "Bezahlt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "paymentType",
|
||||||
|
label: "Zahlart",
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "description",
|
||||||
|
label: "Beschreibung"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
inventoryitems: {
|
inventoryitems: {
|
||||||
isArchivable: true,
|
isArchivable: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user