Start for Dev Branch

This commit is contained in:
2024-12-20 18:46:52 +01:00
parent a6c1eaf69f
commit acf5d1c2ea
16 changed files with 599 additions and 171 deletions

View File

@@ -23,6 +23,9 @@ let {documentData, openShowModal:openShowModalProp, returnEmit } = props;
const tags = dataStore.getDocumentTags const tags = dataStore.getDocumentTags
const openShowModal = ref(false) const openShowModal = ref(false)
//Functions //Functions
const openDocument = async () => { const openDocument = async () => {
//selectedDocument.value = doc //selectedDocument.value = doc
@@ -137,7 +140,7 @@ const updateDocumentAssignment = async () => {
</script> </script>
<template> <template>
<div class="documentListItem" @click="returnEmit ? $emit('clicked', documentData.id) : openShowModal = true"> <div :id="`docDisplay-${documentData.id}`" class="documentListItem" @click="returnEmit ? $emit('clicked', documentData.id) : openShowModal = true">
<iframe <iframe
:src="`${documentData.url}#toolbar=0&navpanes=0&scrollbar=0`" :src="`${documentData.url}#toolbar=0&navpanes=0&scrollbar=0`"
class="previewEmbed" class="previewEmbed"
@@ -150,8 +153,8 @@ const updateDocumentAssignment = async () => {
:src="documentData.url" :src="documentData.url"
/> />
<!-- TODO: Remove Scrollbar --> <!-- TODO: Remove Scrollbar -->
<UTooltip class="w-full" :text="documentData.path.split('_')[documentData.path.split('_').length -1]"> <UTooltip class="w-full" :text="documentData.path.split('/')[documentData.path.split('/').length -1]">
<p class="truncate my-3">{{documentData.path.split("_")[documentData.path.split("_").length -1]}}</p> <p class="truncate my-3">{{documentData.path.split("/")[documentData.path.split("/").length -1]}}</p>
</UTooltip> </UTooltip>
<InputGroup class="mt-3 flex-wrap"> <InputGroup class="mt-3 flex-wrap">
@@ -226,6 +229,14 @@ const updateDocumentAssignment = async () => {
> >
Archivieren Archivieren
</UButton> </UButton>
<UButton
:to="documentData.url"
variant="outline"
icon="i-heroicons-arrow-top-right-on-square"
target="_blank"
>
Öffnen
</UButton>
<!-- <UButton <!-- <UButton
v-if="documentData.tags.includes('Eingangsrechnung')" v-if="documentData.tags.includes('Eingangsrechnung')"
@click="createVendorInvoice" @click="createVendorInvoice"
@@ -233,11 +244,11 @@ const updateDocumentAssignment = async () => {
Eingangsrechnung erstellen Eingangsrechnung erstellen
</UButton>--> </UButton>-->
</UButtonGroup> </UButtonGroup>
<br> <!-- <br>
<a <a
:href="documentData.url" :href="documentData.url"
target="_blank" target="_blank"
>In neuen Tab anzeigen</a> >In neuen Tab anzeigen</a>-->
<UFormGroup <UFormGroup
label="Tags ändern:" label="Tags ändern:"

View File

@@ -1,4 +1,4 @@
<script setup lang="ts"> <script setup >
import { format, isToday } from 'date-fns' import { format, isToday } from 'date-fns'
const props = defineProps({ const props = defineProps({
@@ -12,15 +12,15 @@ const props = defineProps({
} }
}) })
const emit = defineEmits(['update:modelValue']) const emit = defineEmits(['update:modelValue','emailSelected'])
const mailsRefs = ref<Element[]>([]) const mailsRefs = ref([])
const selectedMail = computed({ const selectedMail = computed({
get() { get() {
return props.modelValue return props.modelValue
}, },
set(value: Mail | null) { set(value) {
emit('update:modelValue', value) emit('update:modelValue', value)
} }
}) })
@@ -30,12 +30,17 @@ watch(selectedMail, () => {
return return
} }
const ref = mailsRefs.value[selectedMail.value.id] const ref1 = mailsRefs.value[selectedMail.value.id]
if (ref) { if (ref1) {
ref.scrollIntoView({ block: 'nearest' }) ref1.scrollIntoView({ block: 'nearest' })
} }
}) })
const changeMail = (mail) => {
selectedMail.value = mail
emit("emailSelected")
}
defineShortcuts({ defineShortcuts({
arrowdown: () => { arrowdown: () => {
const index = props.mails.findIndex((mail) => mail.id === selectedMail.value?.id) const index = props.mails.findIndex((mail) => mail.id === selectedMail.value?.id)
@@ -59,15 +64,15 @@ defineShortcuts({
</script> </script>
<template> <template>
<UDashboardPanelContent class="p-0"> <div class="p-0 scrollCont overflow-scroll">
<div v-for="(mail, index) in mails" :key="index" :ref="el => { mailsRefs[mail.id] = el as Element }"> <div v-for="(mail, index) in mails" :key="index" :ref="el => { mailsRefs[mail.id] = el }">
<div <div
class="p-4 text-sm cursor-pointer border-l-2" class="p-4 text-sm cursor-pointer border-l-2"
:class="[ :class="[
mail.unread ? 'text-gray-900 dark:text-white' : 'text-gray-600 dark:text-gray-300', !mail.seen ? 'text-gray-900 dark:text-white' : 'text-gray-600 dark:text-gray-300',
selectedMail && selectedMail.id === mail.id ? 'border-primary-500 dark:border-primary-400 bg-primary-100 dark:bg-primary-900/25' : 'border-white dark:border-gray-900 hover:border-primary-500/25 dark:hover:border-primary-400/25 hover:bg-primary-100/50 dark:hover:bg-primary-900/10' selectedMail && selectedMail.id === mail.id ? 'border-primary-500 dark:border-primary-400 bg-primary-100 dark:bg-primary-900/25' : 'border-white dark:border-gray-900 hover:border-primary-500/25 dark:hover:border-primary-400/25 hover:bg-primary-100/50 dark:hover:bg-primary-900/10'
]" ]"
@click="selectedMail = mail" @click="changeMail(mail)"
> >
<div class="flex items-center justify-between" :class="[mail.unread && 'font-semibold']"> <div class="flex items-center justify-between" :class="[mail.unread && 'font-semibold']">
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
@@ -82,11 +87,17 @@ defineShortcuts({
{{ mail.subject }} {{ mail.subject }}
</p> </p>
<p class="text-gray-400 dark:text-gray-500 line-clamp-1"> <p class="text-gray-400 dark:text-gray-500 line-clamp-1">
{{ mail.body }} {{ mail.text }}
</p> </p>
</div> </div>
<UDivider /> <UDivider />
</div> </div>
</UDashboardPanelContent> </div>
</template> </template>
<style>
.scrollCont {
height: 90vh;
}
</style>

View File

@@ -17,11 +17,11 @@ defineProps({
<UDashboardPanelContent> <UDashboardPanelContent>
<div class="flex justify-between"> <div class="flex justify-between">
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<UAvatar v-bind="mail.from.avatar" :alt="mail.from.name" size="lg" /> <UAvatar v-bind="mail.from.avatar" :alt="mail.from.name||mail.from.address" size="lg" />
<div class="min-w-0"> <div class="min-w-0">
<p class="text-gray-900 dark:text-white font-semibold"> <p class="text-gray-900 dark:text-white font-semibold">
{{ mail.from.name }} {{ mail.from.name ||mail.from.address}}
</p> </p>
<p class="text-gray-500 dark:text-gray-400 font-medium"> <p class="text-gray-500 dark:text-gray-400 font-medium">
{{ mail.subject }} {{ mail.subject }}
@@ -36,18 +36,33 @@ defineProps({
<UDivider class="my-5" /> <UDivider class="my-5" />
<div class="flex-1"> <InputGroup class="mb-3">
<p class="text-lg"> <UButton
{{ mail.body }} v-for="attachment in mail.attachments"
</p> icon="i-heroicons-arrow-down-tray"
disabled
>
{{attachment.filename}}
</UButton>
</InputGroup>
<div class="contentscroll overflow-scroll">
<iframe class="flex-1 h-full w-full" :srcdoc="mail.html" v-if="mail.html"/>
<!-- <div class="flex-1" v-html="mail.html" v-if="mail.html">
</div>-->
<div class="flex-1" v-else>
<p>{{mail.text}}</p>
</div>
</div> </div>
<UDivider class="my-5" />
<form @submit.prevent>
<UTextarea color="gray" required size="xl" :rows="5" :placeholder="`Reply to ${mail.from.name}`">
<UButton type="submit" color="black" label="Send" icon="i-heroicons-paper-airplane" class="absolute bottom-2.5 right-3.5" />
</UTextarea>
</form>
</UDashboardPanelContent> </UDashboardPanelContent>
</template> </template>
<style>
.contentscroll{
height: 80vh;
}
</style>

View File

@@ -1,7 +1,13 @@
<script setup> <script setup>
import {useRole} from "~/composables/useRole.js";
const dataStore = useDataStore() const dataStore = useDataStore()
const route = useRoute() const route = useRoute()
const role = useRole()
console.log(role)
console.log(role.checkRight("projects"))
const links = computed(() => { const links = computed(() => {
return [ return [
{ {
@@ -36,7 +42,7 @@ const links = computed(() => {
icon: "i-heroicons-calendar-days" icon: "i-heroicons-calendar-days"
}] : [], }] : [],
{ {
label: "Dokumente", label: "Dateien",
to: "/documents", to: "/documents",
icon: "i-heroicons-document" icon: "i-heroicons-document"
}, },
@@ -202,37 +208,22 @@ const links = computed(() => {
}, },
] ]
}, },
/*... dataStore.ownTenant.features.projects ? [{ ... role.checkRight("checks") ? [{
label: "Projekte",
defaultOpen: false,
icon: "i-heroicons-clipboard-document-check",
children: [
...dataStore.ownTenant.projecttypes.map(i => {
return {
label: i.label,
to: `/projects?type=${i.label}`,
icon: i.icon
}
})
]
},] : [],*/
{
label: "Überprüfungen", label: "Überprüfungen",
to: "/checks", to: "/checks",
icon: "i-heroicons-magnifying-glass" icon: "i-heroicons-magnifying-glass"
}, },] : [],
... dataStore.ownTenant.features.projects ? [{ ... (role.checkRight("projects") && dataStore.ownTenant.features.projects) ? [{
label: "Projekte", label: "Projekte",
to: "/projects", to: "/projects",
icon: "i-heroicons-clipboard-document-check" icon: "i-heroicons-clipboard-document-check"
},] : [], },] : [],
... dataStore.ownTenant.features.contracts ? [{ ... (role.checkRight("contracts") && dataStore.ownTenant.features.contracts) ? [{
label: "Verträge", label: "Verträge",
to: "/contracts", to: "/contracts",
icon: "i-heroicons-clipboard-document" icon: "i-heroicons-clipboard-document"
}] : [], }] : [],
... dataStore.ownTenant.features.objects ? [{ ... (role.checkRight("objects") && dataStore.ownTenant.features.objects) ? [{
label: "Objekte", label: "Objekte",
to: "/plants", to: "/plants",
icon: "i-heroicons-clipboard-document" icon: "i-heroicons-clipboard-document"

View File

@@ -622,7 +622,7 @@ export const useCreatePdf = async (invoiceData,backgroundSourceBuffer) => {
maxWidth: 240 maxWidth: 240
}) })
console.log(row.text.match(/.{1,35}/g)) console.log(row)
if(invoiceData.type !== "deliveryNotes") { if(invoiceData.type !== "deliveryNotes") {
pages[pageCounter - 1].drawText(row.text.match(/.{1,35}/g).join("\n"), { pages[pageCounter - 1].drawText(row.text.match(/.{1,35}/g).join("\n"), {
@@ -933,6 +933,8 @@ export const useCreatePdf = async (invoiceData,backgroundSourceBuffer) => {
//console.log(rowHeight) //console.log(rowHeight)
//rowHeight += 25 //rowHeight += 25
let endTextDiff = 45
if(invoiceData.type !== "deliveryNotes"){ if(invoiceData.type !== "deliveryNotes"){
pages[pageCounter - 1].drawRectangle({ pages[pageCounter - 1].drawRectangle({
...getCoordinatesForPDFLib(20,rowHeight, page1), ...getCoordinatesForPDFLib(20,rowHeight, page1),
@@ -1012,11 +1014,86 @@ export const useCreatePdf = async (invoiceData,backgroundSourceBuffer) => {
maxWidth: 240, maxWidth: 240,
font:fontBold font:fontBold
}) })
if(invoiceData.total.totalGrossAlreadyPaid !== "0,00 €") {
pages[pageCounter - 1].drawText("Bereits bezahlt:", {
...getCoordinatesForPDFLib(21,rowHeight+21, page1),
size:11,
color:rgb(0,0,0),
lineHeight:11,
opacity: 1,
maxWidth: 240,
font: fontBold
})
pages[pageCounter - 1].drawText(invoiceData.total.totalGrossAlreadyPaid, {
y: getCoordinatesForPDFLib(21,rowHeight+21,page1).y,
x: getCoordinatesForPDFLib(21,rowHeight+21,page1).x + 500 - fontBold.widthOfTextAtSize(invoiceData.total.totalGrossAlreadyPaid,11),
size:11,
color:rgb(0,0,0),
lineHeight:11,
opacity: 1,
maxWidth: 240,
font:fontBold
})
pages[pageCounter - 1].drawRectangle({
...getCoordinatesForPDFLib(20,rowHeight +32, page1),
width: 180 * 2.83,
height: 8 * 2.83,
color: rgb(0,0,0),
opacity: 0.25
})
pages[pageCounter - 1].drawText("Offene Summe:", {
...getCoordinatesForPDFLib(21,rowHeight+29, page1),
size:11,
color:rgb(0,0,0),
lineHeight:11,
opacity: 1,
maxWidth: 240,
font: fontBold
})
pages[pageCounter - 1].drawText(invoiceData.total.totalSumToPay, {
y: getCoordinatesForPDFLib(21,rowHeight+29,page1).y,
x: getCoordinatesForPDFLib(21,rowHeight+29,page1).x + 500 - fontBold.widthOfTextAtSize(invoiceData.total.totalSumToPay,11),
size:11,
color:rgb(0,0,0),
lineHeight:11,
opacity: 1,
maxWidth: 240,
font:fontBold
})
invoiceData.usedAdvanceInvoices.forEach((advanceInvoice,index) => {
//For Each Advance Invoice
pages[pageCounter - 1].drawText(String(advanceInvoice.documentNumber), {
...getCoordinatesForPDFLib(21,rowHeight+36+5*index, page1),
size:11,
color:rgb(0,0,0),
lineHeight:11,
opacity: 1,
maxWidth: 240
})
})
} else {
endTextDiff = 22
}
} }
pages[pageCounter - 1].drawText(invoiceData.endText,{ pages[pageCounter - 1].drawText(invoiceData.endText,{
...getCoordinatesForPDFLib(20,rowHeight+22, page1), ...getCoordinatesForPDFLib(20,rowHeight+endTextDiff, page1),
size: 10, size: 10,
color: rgb(0,0,0), color: rgb(0,0,0),
lineHeight: 10, lineHeight: 10,

114
composables/useRole.js Normal file
View File

@@ -0,0 +1,114 @@
/*const rights2 = ref({
projects: {
label: "Projekte",
children: {
viewOwnProjects: {label: "Eigene Projekte sehen"},
viewAllProjects: {label: "Alle Projekte sehen"},
}
},
createUser: {label: "Benutzer erstellen"},
modifyUser: {label: "Benutzer bearbeiten"},
deactivateUser: {label: "Benutzer sperren"},
createProject: {label: "Projekt erstellen"},
createTask: {label: "Aufgabe erstellen"},
viewOwnTasks: {label:"Eigene Aufgaben sehen"},
viewAllTasks: {label: "Alle Aufgaben sehen"},
trackOwnTime: {label:"Eigene Zeite erfassen"},
createOwnTime: {label:"Eigene Zeiten erstellen"},
createTime: {label:"Zeiten erstellen"},
viewOwnTimes: {label:"Eigene Zeiten anzeigen"},
viewTimes: {label:"Zeiten anzeigen"},
})*/
export const useRole = () => {
const supabase = useSupabaseClient()
const dataStore = useDataStore()
const rights = ref({
projects: {
label: "Projekte"
},
"projects-viewAll": {
label: "Alle Projekte einsehen",
parent: "projects"
},
"projects-viewOwn": {
label: "Eigene Projekte einsehen",
parent: "projects"
},
"projects-create": {
label: "Projekte erstellen",
parent: "projects"
},
contracts: {
label: "Verträge"
},
objects: {
label: "Objekte"
},
checks: {
label: "Überprüfungen"
},
})
let role = dataStore.activeProfile.role
/*const checkRight = (right) => {
let rightsToCheck = [right]
if(rights.value[right].parent) {
rightsToCheck.push(rights.value[right].parent)
}
let hasAllNeccessaryRights = false
rightsToCheck.forEach(i => {
if(role.rights.includes(i)){
hasAllNeccessaryRights = true
} else {
hasAllNeccessaryRights = false
}
})
return hasAllNeccessaryRights
}*/
const checkRight = (right) => {
let rightsToCheck = [right]
//console.log(right.split("-"))
if(right.split("-").length > 1) {
rightsToCheck.push(right.split("-")[0])
}
//console.log(rightsToCheck)
let hasAllNeccessaryRights = true
//console.log(role.rights)
rightsToCheck.forEach(i => {
if(!role.rights.includes(i)){
hasAllNeccessaryRights = false
}
})
//console.log(hasAllNeccessaryRights)
return hasAllNeccessaryRights
}
return {
role,
checkRight
}
}

View File

@@ -33,14 +33,12 @@ export const useSupabaseSelectDocuments = async (select = '*', sortColumn = null
.from("documents") .from("documents")
.select(select) .select(select)
.eq("tenant", dataStore.currentTenant) .eq("tenant", dataStore.currentTenant)
.eq("folderPath", folderPath)
.order(sortColumn, {ascending: true})).data .order(sortColumn, {ascending: true})).data
} else { } else {
data = (await supabase data = (await supabase
.from("documents") .from("documents")
.select(select) .select(select)
.eq("tenant", dataStore.currentTenant) .eq("tenant", dataStore.currentTenant)).data
.eq("folderPath",folderPath)).data
} }

View File

@@ -57,7 +57,8 @@ const itemInfo = ref({
dateDirection: "Rückwirkend", dateDirection: "Rückwirkend",
}, },
letterhead: null, letterhead: null,
agriculture: {} agriculture: {},
usedAdvanceInvoices: []
}) })
@@ -71,7 +72,10 @@ const selectedProductcategorie = ref(null)
const services = ref([]) const services = ref([])
const servicecategories = ref([]) const servicecategories = ref([])
const selectedServicecategorie = ref(null) const selectedServicecategorie = ref(null)
const customers = ref([])
const contacts = ref([])
const loaded = ref(false)
const setupPage = async () => { const setupPage = async () => {
letterheads.value = (await useSupabaseSelect("letterheads","*")).filter(i => i.documentTypes.length === 0 || i.documentTypes.includes(itemInfo.value.type)) letterheads.value = (await useSupabaseSelect("letterheads","*")).filter(i => i.documentTypes.length === 0 || i.documentTypes.includes(itemInfo.value.type))
@@ -81,6 +85,8 @@ const setupPage = async () => {
servicecategories.value = (await useSupabaseSelect("servicecategories","*")) servicecategories.value = (await useSupabaseSelect("servicecategories","*"))
products.value = (await useSupabaseSelect("products","*")) products.value = (await useSupabaseSelect("products","*"))
productcategories.value = (await useSupabaseSelect("productcategories","*")) productcategories.value = (await useSupabaseSelect("productcategories","*"))
customers.value = (await useSupabaseSelect("customers","*","customerNumber"))
contacts.value = (await useSupabaseSelect("contacts","*"))
if(productcategories.value.length > 0) selectedProductcategorie.value = productcategories.value[0].id if(productcategories.value.length > 0) selectedProductcategorie.value = productcategories.value[0].id
if(servicecategories.value.length > 0) selectedServicecategorie.value = servicecategories.value[0].id if(servicecategories.value.length > 0) selectedServicecategorie.value = servicecategories.value[0].id
@@ -165,13 +171,33 @@ const setupPage = async () => {
} }
if(route.query.project) itemInfo.value.project = Number(route.query.project) if(route.query.project) {
itemInfo.value.project = Number(route.query.project)
checkForOpenAdvanceInvoices()
}
if(route.query.contact) itemInfo.value.contact = Number(route.query.contact) if(route.query.contact) itemInfo.value.contact = Number(route.query.contact)
if(route.query.customer) { if(route.query.customer) {
itemInfo.value.customer = Number(route.query.customer) itemInfo.value.customer = Number(route.query.customer)
setCustomerData() setCustomerData()
} }
} }
loaded.value = true
}
const openAdvanceInvoices = ref([])
const checkForOpenAdvanceInvoices = async () => {
const {data,error} = await supabase.from("createddocuments").select().eq("project", itemInfo.value.project).eq("advanceInvoiceResolved", false).eq("type","advanceInvoices")
console.log(data)
openAdvanceInvoices.value = data
}
const addAdvanceInvoiceToInvoice = (advanceInvoice) => {
itemInfo.value.usedAdvanceInvoices.push(advanceInvoice)
} }
const setDocumentTypeConfig = (withTexts = false) => { const setDocumentTypeConfig = (withTexts = false) => {
@@ -201,7 +227,6 @@ const setDocumentTypeConfig = (withTexts = false) => {
const setCustomerData = () => { const setCustomerData = () => {
let customer = dataStore.getCustomerById(itemInfo.value.customer) let customer = dataStore.getCustomerById(itemInfo.value.customer)
itemInfo.value.contact = null itemInfo.value.contact = null
if(customer) { if(customer) {
@@ -210,6 +235,8 @@ const setCustomerData = () => {
itemInfo.value.address.city = customer.infoData.city itemInfo.value.address.city = customer.infoData.city
itemInfo.value.address.special = customer.infoData.special itemInfo.value.address.special = customer.infoData.special
} }
} }
const setContactPersonData = async () => { const setContactPersonData = async () => {
@@ -256,8 +283,6 @@ const importPositions = () => {
} }
} }
const getRowAmount = (row) => { const getRowAmount = (row) => {
return String(Number(Number(row.quantity) * Number(row.price) * (1 - Number(row.discountPercent) /100) ).toFixed(2)).replace('.',',') return String(Number(Number(row.quantity) * Number(row.price) * (1 - Number(row.discountPercent) /100) ).toFixed(2)).replace('.',',')
} }
@@ -332,13 +357,6 @@ const addPosition = (mode) => {
} }
const showEditRowDescription = ref(false)
const rowToEdit = ref("")
const removePosition = (id) => { const removePosition = (id) => {
let rows = itemInfo.value.rows.filter(row => row.id !== id) let rows = itemInfo.value.rows.filter(row => row.id !== id)
/*rows = rows.sort((a,b) => a.pos - b.pos) /*rows = rows.sort((a,b) => a.pos - b.pos)
@@ -353,6 +371,15 @@ const removePosition = (id) => {
} }
const getRowMargin = (row) => {
if(row.mode === "normal" && row.product) {
let purchasePrice = dataStore.getProductById(row.product).purchasePrice || 0
return row.price - purchasePrice
} else {
return 0
}
}
const findDocumentErrors = computed(() => { const findDocumentErrors = computed(() => {
let errors = [] let errors = []
@@ -405,6 +432,7 @@ const tabItems = computed(() => {
const renderCurrency = (value, currency = "€") => { const renderCurrency = (value, currency = "€") => {
return Number(value).toFixed(2).replace(".",",") + " €" return Number(value).toFixed(2).replace(".",",") + " €"
} }
const documentTotal = computed(() => { const documentTotal = computed(() => {
let totalNet = 0 let totalNet = 0
let total19 = 0 let total19 = 0
@@ -412,7 +440,7 @@ const documentTotal = computed(() => {
itemInfo.value.rows.forEach(row => { itemInfo.value.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(2) let rowPrice = Number(Number(row.quantity) * Number(row.price) * (1 - Number(row.discountPercent) /100) ).toFixed(3)
totalNet = totalNet + Number(rowPrice) totalNet = totalNet + Number(rowPrice)
if(row.taxPercent === 19) { if(row.taxPercent === 19) {
@@ -421,11 +449,33 @@ const documentTotal = computed(() => {
} }
}) })
totalGross = totalNet + total19
let totalGrossAlreadyPaid = 0
itemInfo.value.usedAdvanceInvoices.forEach(advanceInvoiceId => {
let advanceInvoice = openAdvanceInvoices.value.find(i => i.id === advanceInvoiceId)
let priceNet = advanceInvoice.rows.find(i => i.advanceInvoiceData).price
let partSum = priceNet * ((100 + advanceInvoice.rows.find(i => i.advanceInvoiceData).taxPercent) / 100)
totalGrossAlreadyPaid += partSum
})
console.log(totalGrossAlreadyPaid)
let sumToPay = totalGross - totalGrossAlreadyPaid
return { return {
totalNet: `${String(totalNet.toFixed(2)).replace(".",",")}`, totalNet: `${String(totalNet.toFixed(2)).replace(".",",")}`,
total19: `${String(total19.toFixed(2)).replace(".",",")}`, total19: `${String(total19.toFixed(2)).replace(".",",")}`,
totalGross: `${String(Number(totalNet + total19).toFixed(2)).replace(".",",")}` totalGross: renderCurrency(totalGross),
totalGrossAlreadyPaid: renderCurrency(totalGrossAlreadyPaid),
totalSumToPay: renderCurrency(sumToPay)
} }
@@ -476,7 +526,6 @@ const processDieselPosition = () => {
itemInfo.value.agriculture = {...itemInfo.value.agriculture, ...agricultureData} itemInfo.value.agriculture = {...itemInfo.value.agriculture, ...agricultureData}
} }
const getDocumentData = () => { const getDocumentData = () => {
let customerData = dataStore.getCustomerById(itemInfo.value.customer) let customerData = dataStore.getCustomerById(itemInfo.value.customer)
@@ -532,6 +581,7 @@ const getDocumentData = () => {
return { return {
vorname:contactData && contactData.firstName, vorname:contactData && contactData.firstName,
nachname: contactData && contactData.lastName, nachname: contactData && contactData.lastName,
kundenname: customerData && customerData.name,
zahlungsziel_in_tagen:itemInfo.paymentDays, zahlungsziel_in_tagen:itemInfo.paymentDays,
diesel_gesamtverbrauch: (itemInfo.agriculture && itemInfo.agriculture.dieselUsageTotal) && itemInfo.agriculture.dieselUsageTotal diesel_gesamtverbrauch: (itemInfo.agriculture && itemInfo.agriculture.dieselUsageTotal) && itemInfo.agriculture.dieselUsageTotal
} }
@@ -566,7 +616,10 @@ const getDocumentData = () => {
startText: templateStartText(generateContext(itemInfo.value, contactData)), startText: templateStartText(generateContext(itemInfo.value, contactData)),
rows: rows, rows: rows,
total: documentTotal.value, total: documentTotal.value,
agriculture: itemInfo.value.agriculture agriculture: itemInfo.value.agriculture,
usedAdvanceInvoices: itemInfo.value.usedAdvanceInvoices.map(i => {
return openAdvanceInvoices.value.find(x => x.id === i)
})
} }
//console.log(returnData) //console.log(returnData)
@@ -574,7 +627,6 @@ const getDocumentData = () => {
return returnData return returnData
} }
const showDocument = ref(false) const showDocument = ref(false)
const uri = ref("") const uri = ref("")
const generateDocument = async () => { const generateDocument = async () => {
@@ -712,7 +764,8 @@ const saveDocument = async (state) => {
contactPerson: itemInfo.value.contactPerson, contactPerson: itemInfo.value.contactPerson,
linkedDocument: itemInfo.value.linkedDocument, linkedDocument: itemInfo.value.linkedDocument,
agriculture: itemInfo.value.agriculture, agriculture: itemInfo.value.agriculture,
letterhead: itemInfo.value.letterhead letterhead: itemInfo.value.letterhead,
usedAdvanceInvoices: itemInfo.value.usedAdvanceInvoices
} }
if(route.params.id) { if(route.params.id) {
@@ -796,7 +849,7 @@ setupPage()
</UDashboardNavbar> </UDashboardNavbar>
<UDashboardPanelContent> <UDashboardPanelContent>
<UTabs class="p-5" :items="tabItems" @change="onChangeTab"> <UTabs class="p-5" :items="tabItems" @change="onChangeTab" v-if="loaded">
<template #item="{item}"> <template #item="{item}">
@@ -940,11 +993,9 @@ setupPage()
<UFormGroup <UFormGroup
label="Kunde:" label="Kunde:"
> >
<InputGroup <div class="flex flex-row">
class="w-full"
>
<USelectMenu <USelectMenu
:options="dataStore.customers" :options="customers"
option-attribute="name" option-attribute="name"
value-attribute="id" value-attribute="id"
:search-attributes="['name']" :search-attributes="['name']"
@@ -952,14 +1003,15 @@ setupPage()
searchable-placeholder="Suche..." searchable-placeholder="Suche..."
v-model="itemInfo.customer" v-model="itemInfo.customer"
@change="setCustomerData" @change="setCustomerData"
class="flex-auto" class="flex-auto mr-2"
> >
<UButton <UButton
:color="itemInfo.customer ? 'primary' : 'rose'" :color="itemInfo.customer ? 'primary' : 'rose'"
variant="outline" variant="outline"
class="w-full"> class="w-full"
<span class="truncate">{{dataStore.getCustomerById(itemInfo.customer) ? dataStore.getCustomerById(itemInfo.customer).name : "Kein Kunde ausgewählt"}}</span> >
<!-- <span class="truncate text-left">{{dataStore.getCustomerById(itemInfo.customer) ? dataStore.getCustomerById(itemInfo.customer).name : "Kein Kunde ausgewählt"}}</span>-->
{{dataStore.getCustomerById(itemInfo.customer) ? dataStore.getCustomerById(itemInfo.customer).name : "Kein Kunde ausgewählt"}}
<UIcon name="i-heroicons-chevron-right-20-solid" class="w-5 h-5 transition-transform text-gray-400 dark:text-gray-500" :class="['transform rotate-90']" /> <UIcon name="i-heroicons-chevron-right-20-solid" class="w-5 h-5 transition-transform text-gray-400 dark:text-gray-500" :class="['transform rotate-90']" />
</UButton> </UButton>
@@ -969,11 +1021,14 @@ setupPage()
</USelectMenu> </USelectMenu>
<UButton <UButton
variant="outline" variant="outline"
class="w-25"
v-if="itemInfo.customer" v-if="itemInfo.customer"
icon="i-heroicons-arrow-right-end-on-rectangle" icon="i-heroicons-arrow-right-end-on-rectangle"
@click="router.push(`/customers/show/${itemInfo.customer}`)" @click="router.push(`/customers/show/${itemInfo.customer}`)"
>Kunde</UButton> >Kunde</UButton>
</InputGroup> </div>
<UAlert <UAlert
v-if="itemInfo.customer" v-if="itemInfo.customer"
@@ -997,7 +1052,7 @@ setupPage()
> >
<InputGroup> <InputGroup>
<USelectMenu <USelectMenu
:options="dataStore.getContactsByCustomerId(itemInfo.customer)" :options="contacts.filter(i => i.customer === itemInfo.customer)"
option-attribute="fullName" option-attribute="fullName"
value-attribute="id" value-attribute="id"
:search-attributes="['name']" :search-attributes="['name']"
@@ -1121,9 +1176,13 @@ setupPage()
</UFormGroup> </UFormGroup>
<UFormGroup <UFormGroup
label="Zahlungziel in Tagen:" class="w-full"
class="flex-auto"
> >
<template #label>
<span class="truncate">
Zahlungsziel in Tagen:
</span>
</template>
<UInput <UInput
type="number" type="number"
v-model="itemInfo.paymentDays" v-model="itemInfo.paymentDays"
@@ -1169,6 +1228,7 @@ setupPage()
:search-attributes="['name']" :search-attributes="['name']"
class="w-full" class="w-full"
:disabled="!itemInfo.customer" :disabled="!itemInfo.customer"
@change="checkForOpenAdvanceInvoices"
> >
<template #label> <template #label>
{{dataStore.getProjectById(itemInfo.project) ? dataStore.getProjectById(itemInfo.project).name : "Kein Projekt ausgewählt"}} {{dataStore.getProjectById(itemInfo.project) ? dataStore.getProjectById(itemInfo.project).name : "Kein Projekt ausgewählt"}}
@@ -1270,9 +1330,6 @@ setupPage()
</div> </div>
<UDivider <UDivider
class="my-3" class="my-3"
/> />
@@ -1389,6 +1446,7 @@ setupPage()
<UInput <UInput
v-model="row.text" v-model="row.text"
placeholder="Name" placeholder="Name"
class="min-w-40"
/> />
</td> </td>
<td <td
@@ -1558,7 +1616,8 @@ setupPage()
<UInput <UInput
v-model="row.price" v-model="row.price"
type="number" type="number"
step="0.01" step="0.001"
:color="getRowMargin(row) > 0 ? 'primary' : 'rose'"
> >
<template #trailing> <template #trailing>
<span class="text-gray-500 dark:text-gray-400 text-xs">EUR</span> <span class="text-gray-500 dark:text-gray-400 text-xs">EUR</span>
@@ -1715,18 +1774,21 @@ setupPage()
<UButton <UButton
@click="addPosition('service')" @click="addPosition('service')"
class="mt-3" class="mt-3"
:disabled="itemInfo.type === 'advanceInvoices'"
> >
+ Leistung + Leistung
</UButton> </UButton>
<UButton <UButton
@click="addPosition('normal')" @click="addPosition('normal')"
class="mt-3" class="mt-3"
:disabled="itemInfo.type === 'advanceInvoices'"
> >
+ Artikel + Artikel
</UButton> </UButton>
<UButton <UButton
@click="addPosition('free')" @click="addPosition('free')"
class="mt-3" class="mt-3"
:disabled="itemInfo.type === 'advanceInvoices'"
> >
+ Freie Position + Freie Position
</UButton> </UButton>
@@ -1750,6 +1812,44 @@ setupPage()
</UButton> </UButton>
</InputGroup> </InputGroup>
<UDivider
class="mt-5 mb-3"
v-if="openAdvanceInvoices.length > 0"
>
Noch nicht abgerechnete Abschlagsrechnungen
</UDivider>
<!-- Abzurechnende Abschlagsrechnungen -->
<div
v-for="advanceInvoice in openAdvanceInvoices"
:key="advanceInvoice.id"
class="flex flex-row justify-between my-2"
>
<span>{{advanceInvoice.documentNumber}}</span>
<span>Summe: {{renderCurrency(advanceInvoice.rows[0].price * (1 + advanceInvoice.rows[0].taxPercent/100))}}</span>
<InputGroup>
<UButton
@click="addAdvanceInvoiceToInvoice(advanceInvoice.id)"
:disabled="itemInfo.usedAdvanceInvoices.includes(advanceInvoice.id)"
class="mr-2"
>
Verwenden
</UButton>
<UButton
@click="itemInfo.usedAdvanceInvoices = itemInfo.usedAdvanceInvoices.filter(i => i !== advanceInvoice.id)"
:disabled="!itemInfo.usedAdvanceInvoices.includes(advanceInvoice.id)"
color="rose"
variant="outline"
>
X
</UButton>
</InputGroup>
</div>
<UDivider class="my-3" v-if="itemInfo.rows.length > 0"/>
<div class="w-full flex justify-end" v-if="itemInfo.type !== 'deliveryNotes'"> <div class="w-full flex justify-end" v-if="itemInfo.type !== 'deliveryNotes'">
<table class="w-1/3" v-if="itemInfo.rows.length > 0"> <table class="w-1/3" v-if="itemInfo.rows.length > 0">
<tr> <tr>
@@ -1764,6 +1864,19 @@ setupPage()
<td class="font-bold">Brutto:</td> <td class="font-bold">Brutto:</td>
<td class="text-right">{{documentTotal.totalGross}}</td> <td class="text-right">{{documentTotal.totalGross}}</td>
</tr> </tr>
<tr v-if="documentTotal.totalGrossAlreadyPaid !== '0,00 €'">
<td class="font-bold">Bereits bezahlt:</td>
<td class="text-right">{{documentTotal.totalGrossAlreadyPaid}}</td>
</tr>
<tr v-if="documentTotal.totalGrossAlreadyPaid !== '0,00 €'">
<td class="font-bold">Offene Summe:</td>
<td class="text-right">{{documentTotal.totalSumToPay}}</td>
</tr>
</table> </table>
</div> </div>
@@ -1815,6 +1928,7 @@ setupPage()
</div> </div>
</template> </template>
</UTabs> </UTabs>
<UProgress animation="carousel" v-else/>
</UDashboardPanelContent> </UDashboardPanelContent>

View File

@@ -2,7 +2,7 @@
import {BlobReader, BlobWriter, ZipWriter} from "@zip.js/zip.js"; import {BlobReader, BlobWriter, ZipWriter} from "@zip.js/zip.js";
import {useSupabaseSelectDocuments} from "~/composables/useSupabase.js"; import {useSupabaseSelectDocuments, useSupabaseSelectSingle} from "~/composables/useSupabase.js";
definePageMeta({ definePageMeta({
middleware: "auth" middleware: "auth"
@@ -11,6 +11,8 @@ const dataStore = useDataStore()
const supabase = useSupabaseClient() const supabase = useSupabaseClient()
const user = useSupabaseUser() const user = useSupabaseUser()
const toast = useToast() const toast = useToast()
const router = useRouter()
const route = useRoute()
dataStore.fetchDocuments() dataStore.fetchDocuments()
@@ -19,29 +21,31 @@ const uploadInProgress = ref(false)
const fileUploadFormData = ref({ const fileUploadFormData = ref({
tags: ["Eingang"], tags: ["Eingang"],
path: "", path: "",
tenant: dataStore.currentTenant tenant: dataStore.currentTenant,
folder: null
}) })
let tags = dataStore.getDocumentTags let tags = dataStore.getDocumentTags
const selectedTags = ref("Eingang")
const documents = ref([]) const documents = ref([])
const folders = ref([]) const folders = ref([])
const selectedPath = ref("_")
const currentFolder = ref(null)
const loadingDocs = ref(false) const loadingDocs = ref(false)
const isDragTarget = ref(false) const isDragTarget = ref(false)
const setupPage = async () => { const setupPage = async () => {
//documents.value = await useSupabaseSelectDocuments("*, project(id,name), customer(id,name), contract(id,name), vendor(id,name), plant(id,name), vehicle(id,licensePlate), product(id,name), profile(id,fullName) ") folders.value = await useSupabaseSelect("folders")
//documents.value = await useSupabaseSelectDocuments("*",null, selectedPath.value)
//console.log(documents.value)
folders.value = dataStore.ownTenant.documentFolders documents.value = await useSupabaseSelectDocuments("*",null)
documents.value = await useSupabaseSelectDocuments("*",null,selectedPath.value) if(route.query) {
//await supabase.from("documents").select().eq("folderPath",selectedPath.value).eq("tenant",dataStore.currentTenant) if(route.query.folder) {
console.log(route.query.folder)
//console.log(await supabase.from("documents").select().eq("folderPath",selectedPath.value)) currentFolder.value = await useSupabaseSelectSingle("folders", route.query.folder)
}
}
const dropZone = document.getElementById("drop_zone") const dropZone = document.getElementById("drop_zone")
@@ -71,37 +75,63 @@ const setupPage = async () => {
setupPage() setupPage()
const currentFolders = computed(() => { const currentFolders = computed(() => {
if(folders.value.length > 0) { if(folders.value.length > 0) {
/*console.log(folders.value[0].path.split("/").filter(x => x.length > 0))
console.log(selectedPath.value.split("/").filter(x => x.length > 0))*/
let tempFolders = folders.value.filter(i => (i.path.split("_").filter(x => x.length > 0) || []).length === selectedPath.value.split("_").filter(x => x.length > 0).length + 1) let tempFolders = folders.value.filter(i => currentFolder.value ? i.parent === currentFolder.value.id : !i.parent)
tempFolders = tempFolders.filter(i => i.path.includes(selectedPath.value))
return tempFolders return tempFolders
} } else return []
}) })
const breadcrumbLinks = computed(() => { const breadcrumbLinks = computed(() => {
if(currentFolder.value) {
let parents = []
const addParent = (parent) => {
parents.push(parent)
if(parent.parent) {
addParent(folders.value.find(i => i.id === currentFolder.value.parent))
}
}
if(currentFolder.value.parent) {
addParent(folders.value.find(i => i.id === currentFolder.value.parent))
}
return [{ return [{
label: "Home", label: "Home",
click: () => { click: () => {
changePath("_") changeFolder(null)
}, },
icon: "i-heroicons-folder" icon: "i-heroicons-folder"
},...selectedPath.value.split("_").filter(x => x.length > 0).map((i,index) => { },
let re = new RegExp(".+?" + i ) ...parents.map(i => {
let path = selectedPath.value.match(re)[0]
return { return {
label: folders.value.find(x => x.path === path).name ||path, label: folders.value.find(x => x.id === i.id).name,
click: () => { click: () => {
changePath(path) changeFolder(i)
}, },
icon: "i-heroicons-folder" icon: "i-heroicons-folder"
} }
})] }),
{
label: currentFolder.value.name,
click: () => {
changeFolder(currentFolder.value)
},
icon: "i-heroicons-folder"
}]
} else {
return [{
label: "Home",
click: () => {
changeFolder(null)
},
icon: "i-heroicons-folder"
}]
}
}) })
@@ -110,23 +140,22 @@ const breadcrumbLinks = computed(() => {
const filteredDocuments = computed(() => { const filteredDocuments = computed(() => {
/*if(selectedTags.value !== "Archiviert") { return documents.value.filter(i => currentFolder.value ? i.folder === currentFolder.value.id : !i.folder)
return documents.value.filter(i => i.tags.find(t => selectedTags.value === t) && !i.tags.includes("Archiviert"))
} else {
return documents.value.filter(i => i.tags.find(t => selectedTags.value === t))
}*/
return documents.value
}) })
const changePath = async (newPath) => { const changeFolder = async (newFolder) => {
loadingDocs.value = true loadingDocs.value = true
selectedPath.value = newPath currentFolder.value = newFolder
if(newFolder) {
fileUploadFormData.value.folder = newFolder.id
await router.push(`/documents?folder=${newFolder.id}`)
} else {
fileUploadFormData.value.folder = null
await router.push(`/documents`)
}
setupPage() setupPage()
} }
@@ -134,7 +163,7 @@ const uploadFiles = async (files) => {
uploadInProgress.value = true; uploadInProgress.value = true;
if(files) { if(files) {
await dataStore.uploadFiles({tags: ["Ablage"],tenant: dataStore.currentTenant, folderPath: selectedPath.value}, files, true) await dataStore.uploadFiles({tags: ["Ablage"],tenant: dataStore.currentTenant,folder: currentFolder.value.id}, files, true)
} else { } else {
await dataStore.uploadFiles(fileUploadFormData.value, document.getElementById("fileUploadInput").files, true) await dataStore.uploadFiles(fileUploadFormData.value, document.getElementById("fileUploadInput").files, true)
@@ -213,7 +242,7 @@ const downloadSelected = async () => {
<template> <template>
<UDashboardNavbar <UDashboardNavbar
title="Dokumente" title="Dateien"
> >
</UDashboardNavbar> </UDashboardNavbar>
@@ -254,7 +283,7 @@ const downloadSelected = async () => {
<a <a
class="w-1/6 folderIcon flex flex-col p-5 m-2" class="w-1/6 folderIcon flex flex-col p-5 m-2"
v-for="folder in currentFolders" v-for="folder in currentFolders"
@click="changePath(folder.path)" @click="changeFolder(folder)"
> >
<UIcon <UIcon
@@ -265,8 +294,8 @@ const downloadSelected = async () => {
</a> </a>
</div> </div>
<UDivider class="my-5" v-if="currentFolders.length > 0">Dokumente</UDivider> <UDivider class="my-5" v-if="currentFolder">{{currentFolder.name}}</UDivider>
<UDivider class="my-5" v-else>Ablage</UDivider>
<div v-if="!loadingDocs"> <div v-if="!loadingDocs">
<DocumentList <DocumentList

View File

@@ -43,6 +43,9 @@
:ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }" :ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }"
@select="(i) => router.push(`/inventoryitems/show/${i.id}`) " @select="(i) => router.push(`/inventoryitems/show/${i.id}`) "
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Inventarartikel anzuzeigen' }" :empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Inventarartikel anzuzeigen' }"
v-model:sort="sortConfig"
sort-mode="manual"
@update:sort="setupPage"
> >
@@ -50,6 +53,8 @@
</template> </template>
<script setup> <script setup>
import {sort} from "fast-sort"
definePageMeta({ definePageMeta({
middleware: "auth" middleware: "auth"
@@ -70,8 +75,13 @@ const dataStore = useDataStore()
const router = useRouter() const router = useRouter()
const items = ref([]) const items = ref([])
const sortConfig = ref({
column: "articleNumber",
direction: "desc"
})
const setupPage = async () => { const setupPage = async () => {
items.value = await useSupabaseSelect("inventoryitems","*", "articleNumber") items.value = await useSupabaseSelect("inventoryitems","*")
} }
setupPage() setupPage()
@@ -96,17 +106,20 @@ const selectedColumns = ref(templateColumns)
const columns = computed(() => templateColumns.filter((column) => selectedColumns.value.includes(column))) const columns = computed(() => templateColumns.filter((column) => selectedColumns.value.includes(column)))
const searchString = ref('') const searchString = ref('')
const filteredRows = computed(() => { const filteredRows = computed(() => {
if(!searchString.value) {
return items.value let sorted = []
if(sortConfig.value.direction === "asc"){
sorted = sort(items.value).asc(i => i[sortConfig.value.column])
} else {
sorted = sort(items.value).desc(i => i[sortConfig.value.column])
} }
return items.value.filter(product => { return useListFilter(searchString.value, sorted)
return Object.values(product).some((value) => {
return String(value).toLowerCase().includes(searchString.value.toLowerCase())
})
})
}) })

View File

@@ -237,7 +237,7 @@ setupPage()
<UInput <UInput
v-model="itemInfo.sellingPrice" v-model="itemInfo.sellingPrice"
type="number" type="number"
steps="0.01" steps="0.001"
> >
<template #trailing> <template #trailing>
<span class="text-gray-500 dark:text-gray-400 text-xs">EUR</span> <span class="text-gray-500 dark:text-gray-400 text-xs">EUR</span>

View File

@@ -0,0 +1,41 @@
<script setup>
const lexofficeActiveFunctions = ref({
sendInvoicesToLexoffice: true,
})
</script>
<template>
<UDashboardNavbar title="Integrationen"></UDashboardNavbar>
<UDashboardPanelContent>
<UAccordion
class="p-5"
color="primary"
variant="soft"
size="sm"
:items="[{ label: 'Lexoffice', content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit' }]"
>
<template #item="{item}">
<div v-if="item.label === 'Lexoffice'">
Scopes:
<UCheckbox v-model="lexofficeActiveFunctions.sendInvoicesToLexoffice" name="notifications" label="Rechnungen an Lexoffice übertragen" />
<UButton>
Kunden importieren
</UButton>
<UButton>
Lieferanten importieren
</UButton>
</div>
</template>
</UAccordion>
</UDashboardPanelContent>
</template>
<style scoped>
</style>

View File

@@ -48,9 +48,9 @@ const projects = ref([])
//Functions //Functions
const setupPage = async () => { const setupPage = async () => {
if(mode.value === "show" ){ if(mode.value === "show" ){
itemInfo.value = await useSupabaseSelectSingle("trackingtrips",route.params.id,"*, trackingDevice(*, vehicle(*))") itemInfo.value = await useSupabaseSelectSingle("trackingtripsgen",route.params.id,"*, trackingDevice(*, vehicle(*))")
} else if(mode.value === "edit") { } else if(mode.value === "edit") {
itemInfo.value = await useSupabaseSelectSingle("trackingtrips",route.params.id,"*") itemInfo.value = await useSupabaseSelectSingle("trackingtripsgen",route.params.id,"*")
} }
@@ -141,8 +141,6 @@ const zoom = ref(6)
</template> </template>
</UDashboardNavbar> </UDashboardNavbar>
<UTabs <UTabs
:items="[{label: 'Informationen'}]" :items="[{label: 'Informationen'}]"
v-if="mode === 'show' && itemInfo.id" v-if="mode === 'show' && itemInfo.id"
@@ -192,9 +190,9 @@ const zoom = ref(6)
</UCard> </UCard>
<UCard class="mt-5"> <UCard class="mt-5">
<Map :markers="[[itemInfo.startLatitude, itemInfo.startLongitude],[itemInfo.endLatitude, itemInfo.endLongitude]]" <Map :markers="[[itemInfo.startLat, itemInfo.startLon],[itemInfo.endLat, itemInfo.endLon]]"
:startMarker="[itemInfo.startLatitude, itemInfo.startLongitude]" :startMarker="[itemInfo.startLat, itemInfo.startLon]"
:endMarker="[itemInfo.endLatitude, itemInfo.endLongitude]" :endMarker="[itemInfo.endLat, itemInfo.endLon]"
/> />

View File

@@ -57,8 +57,12 @@
<template #endTime-data="{row}"> <template #endTime-data="{row}">
{{dayjs(row.endTime).format("DD.MM.YY HH:mm")}} {{dayjs(row.endTime).format("DD.MM.YY HH:mm")}}
</template> </template>
<template #distance-data="{row}"> <template #gpsDistance-data="{row}">
{{row.distance > 0 ? (row.distance/1000).toFixed(2) : 0 }} km {{row.gpsDistance > 0 ? (row.gpsDistance/1000).toFixed(2) : 0 }} km
</template>
<template #usedFuel-data="{row}">
<span v-if="row.endFuelLevel - row.startFuelLevel < 0">{{row.endFuelLevel - row.startFuelLevel}} / {{(row.vehicle.tankSize / 100 * Math.abs(row.endFuelLevel - row.startFuelLevel)).toFixed(2)}}</span>
<span v-else>0</span>
</template> </template>
</UTable> </UTable>
</template> </template>
@@ -106,7 +110,8 @@ const items = ref([])
const selectedItem = ref(0) const selectedItem = ref(0)
const setupPage = async () => { const setupPage = async () => {
items.value = await useSupabaseSelect("trackingtrips","*, trackingDevice(*), vehicle (*)","startTime",false) items.value = await useSupabaseSelect("trackingtripsgen","*, trackingDevice(*), vehicle(*)","startTime",false)
console.log(items.value)
} }
setupPage() setupPage()
@@ -137,9 +142,13 @@ const templateColumns = [
sortable: true sortable: true
}, },
{ {
key: "distance", key: "gpsDistance",
label: "Entfernung:", label: "Entfernung:",
sortable: true sortable: true
},
{
key: "usedFuel",
label: "Kraftstoff:"
} }
] ]
const selectedColumns = ref(templateColumns) const selectedColumns = ref(templateColumns)

View File

@@ -121,6 +121,7 @@ setupPage()
<p>E-Mail: {{currentItem.infoData.email}}</p> <p>E-Mail: {{currentItem.infoData.email}}</p>
<p>Web: {{currentItem.infoData.web}}</p> <p>Web: {{currentItem.infoData.web}}</p>
<p>USt-Id: {{currentItem.infoData.ustid}}</p> <p>USt-Id: {{currentItem.infoData.ustid}}</p>
<p>SEPA Mandat: {{currentItem.hasSEPA ? "Ja" : "Nein"}}</p>
<p>Notizen:<br> {{currentItem.notes}}</p> <p>Notizen:<br> {{currentItem.notes}}</p>
</div> </div>
</UCard> </UCard>
@@ -251,6 +252,13 @@ setupPage()
v-model="itemInfo.infoData.web" v-model="itemInfo.infoData.web"
/> />
</UFormGroup> </UFormGroup>
<UFormGroup
label="SEPA Mandat abgeschlossen:"
>
<UCheckbox
v-model="itemInfo.hasSEPA"
/>
</UFormGroup>
<UFormGroup <UFormGroup
label="USt-Id:" label="USt-Id:"
> >

View File

@@ -313,7 +313,7 @@ export const useDataStore = defineStore('data', () => {
const resources =ref([]) const resources =ref([])
const rights = ref({ /*const rights = ref({
createUser: {label: "Benutzer erstellen"}, createUser: {label: "Benutzer erstellen"},
modifyUser: {label: "Benutzer bearbeiten"}, modifyUser: {label: "Benutzer bearbeiten"},
deactivateUser: {label: "Benutzer sperren"}, deactivateUser: {label: "Benutzer sperren"},
@@ -328,9 +328,9 @@ export const useDataStore = defineStore('data', () => {
createTime: {label:"Zeiten erstellen"}, createTime: {label:"Zeiten erstellen"},
viewOwnTimes: {label:"Eigene Zeiten anzeigen"}, viewOwnTimes: {label:"Eigene Zeiten anzeigen"},
viewTimes: {label:"Zeiten anzeigen"}, viewTimes: {label:"Zeiten anzeigen"},
}) })*/
const roles = ref([ /*const roles = ref([
{ {
key: "tenantAdmin", key: "tenantAdmin",
label: "Firmenadministrator", label: "Firmenadministrator",
@@ -369,12 +369,12 @@ export const useDataStore = defineStore('data', () => {
"viewAllTimes" "viewAllTimes"
] ]
} }
]) ])*/
async function initializeData (userId) { async function initializeData (userId) {
let profileconnections = (await supabase.from("profileconnections").select()).data let profileconnections = (await supabase.from("profileconnections").select()).data
let profiles = (await supabase.from("profiles").select()).data let profiles = (await supabase.from("profiles").select("*, role(*)")).data
let activeProfileConnection = profileconnections.find(i => i.active) let activeProfileConnection = profileconnections.find(i => i.active)
if(activeProfileConnection) { if(activeProfileConnection) {
activeProfile.value = profiles.find(i => i.id === activeProfileConnection.profile_id) activeProfile.value = profiles.find(i => i.id === activeProfileConnection.profile_id)
@@ -521,7 +521,7 @@ export const useDataStore = defineStore('data', () => {
resources.value = [] resources.value = []
} }
function hasRight (right) { /*function hasRight (right) {
const role = profiles.value.find(i => i.id === activeProfile.value.id).role const role = profiles.value.find(i => i.id === activeProfile.value.id).role
const grantedRights = roles.value.find(i => i.key === role).rights const grantedRights = roles.value.find(i => i.key === role).rights
@@ -529,7 +529,7 @@ export const useDataStore = defineStore('data', () => {
return true return true
} else return false } else return false
} }*/
var deepDiffMapper = function () { var deepDiffMapper = function () {
return { return {
@@ -1749,7 +1749,6 @@ export const useDataStore = defineStore('data', () => {
initializeData, initializeData,
changeProfile, changeProfile,
uploadFiles, uploadFiles,
hasRight,
generateHistoryItems, generateHistoryItems,
dataTypes, dataTypes,