Merge branch 'refs/heads/beta'
This commit is contained in:
5
app.vue
5
app.vue
@@ -18,10 +18,13 @@ const setup = async () => {
|
||||
if(await useCapacitor().getIsPhone()) {
|
||||
platform.value = "mobile"
|
||||
}
|
||||
|
||||
const dev = process.dev
|
||||
console.log(dev)
|
||||
}
|
||||
setup()
|
||||
|
||||
const dev = process.dev
|
||||
|
||||
|
||||
Sentry.init({
|
||||
dsn: "https://62e62ff08e1a438591fe5eb4dd9de244@glitchtip.federspiel.software/3",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import {useTempStore} from "~/stores/temp.js";
|
||||
import FloatingActionButton from "~/components/mobile/FloatingActionButton.vue";
|
||||
import EntityTable from "~/components/EntityTable.vue";
|
||||
import EntityListMobile from "~/components/EntityListMobile.vue";
|
||||
|
||||
const props = defineProps({
|
||||
type: {
|
||||
@@ -181,7 +182,14 @@ const filteredRows = computed(() => {
|
||||
</USelectMenu>
|
||||
</template>
|
||||
</UDashboardToolbar>
|
||||
<EntityListMobile
|
||||
v-if="platform === 'mobile'"
|
||||
:type="props.type"
|
||||
:columns="columns"
|
||||
:rows="filteredRows"
|
||||
/>
|
||||
<EntityTable
|
||||
v-else
|
||||
:type="props.type"
|
||||
:columns="columns"
|
||||
:rows="filteredRows"
|
||||
|
||||
129
components/EntityListMobile.vue
Normal file
129
components/EntityListMobile.vue
Normal file
@@ -0,0 +1,129 @@
|
||||
<script setup>
|
||||
/*defineShortcuts({
|
||||
/!*'/': () => {
|
||||
//console.log(searchinput)
|
||||
//searchinput.value.focus()
|
||||
document.getElementById("searchinput").focus()
|
||||
},*!/
|
||||
'Enter': {
|
||||
usingInput: true,
|
||||
handler: () => {
|
||||
router.push(`/standardEntity/${props.type}/show/${props.rows.value[selectedItem.value].id}`)
|
||||
}
|
||||
},
|
||||
'arrowdown': () => {
|
||||
if(selectedItem.value < props.rows.length - 1) {
|
||||
selectedItem.value += 1
|
||||
} else {
|
||||
selectedItem.value = 0
|
||||
}
|
||||
},
|
||||
'arrowup': () => {
|
||||
if(selectedItem.value === 0) {
|
||||
selectedItem.value = props.rows.length - 1
|
||||
} else {
|
||||
selectedItem.value -= 1
|
||||
}
|
||||
}
|
||||
})*/
|
||||
|
||||
const props = defineProps({
|
||||
rows: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: []
|
||||
},
|
||||
columns: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
}
|
||||
})
|
||||
|
||||
const dataStore = useDataStore()
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const dataType = dataStore.dataTypes[props.type]
|
||||
|
||||
const selectedItem = ref(0)
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UDashboardPanelContent class="w-full">
|
||||
<a
|
||||
v-for="item in props.rows"
|
||||
class="my-1"
|
||||
@click="router.push(`/standardEntity/${type}/show/${item.id}`)"
|
||||
>
|
||||
<p class="truncate text-left text-primary text-xl">{{dataType.templateColumns.find(i => i.title).key ? item[dataType.templateColumns.find(i => i.title).key] : null}}</p>
|
||||
<p class="text-sm">
|
||||
{{ dataType.numberRangeHolder ? item[dataType.numberRangeHolder] : null}}
|
||||
|
||||
<span v-for="secondInfo in dataType.templateColumns.filter(i => i.secondInfo)">{{(secondInfo.secondInfoKey && item[secondInfo.key]) ? item[secondInfo.key][secondInfo.secondInfoKey] : item[secondInfo.key]}}</span>
|
||||
|
||||
</p>
|
||||
</a>
|
||||
</UDashboardPanelContent>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- <UTable
|
||||
v-if="dataType && columns"
|
||||
:rows="props.rows"
|
||||
:columns="props.columns"
|
||||
class="w-full"
|
||||
:ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }"
|
||||
@select="(i) => router.push(`/standardEntity/${type}/show/${i.id}`) "
|
||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: `Keine ${dataType.label} anzuzeigen` }"
|
||||
>
|
||||
<template
|
||||
v-for="column in dataType.templateColumns.filter(i => !i.disabledInTable)"
|
||||
v-slot:[`${column.key}-header`]="{row}">
|
||||
<span class="text-nowrap">{{column.label}}</span>
|
||||
</template>
|
||||
<template #name-data="{row}">
|
||||
<span
|
||||
v-if="row.id === props.rows[selectedItem].id"
|
||||
class="text-primary-500 font-bold">{{row.name}}
|
||||
</span>
|
||||
<span v-else>
|
||||
{{row.name}}
|
||||
</span>
|
||||
</template>
|
||||
<template #fullName-data="{row}">
|
||||
<span
|
||||
v-if="row.id === props.rows[selectedItem].id"
|
||||
class="text-primary-500 font-bold">{{row.fullName}}
|
||||
</span>
|
||||
<span v-else>
|
||||
{{row.fullName}}
|
||||
</span>
|
||||
</template>
|
||||
<template #licensePlate-data="{row}">
|
||||
<span
|
||||
v-if="row.id === props.rows[selectedItem].id"
|
||||
class="text-primary-500 font-bold">{{row.licensePlate}}
|
||||
</span>
|
||||
<span v-else>
|
||||
{{row.licensePlate}}
|
||||
</span>
|
||||
</template>
|
||||
<template
|
||||
v-for="column in dataType.templateColumns.filter(i => i.key !== 'name' && i.key !== 'fullName' && i.key !== 'licensePlate' && !i.disabledInTable)"
|
||||
v-slot:[`${column.key}-data`]="{row}">
|
||||
<component v-if="column.component" :is="column.component" :row="row"></component>
|
||||
<span v-else>{{row[column.key] ? `${row[column.key]} ${column.unit ? column.unit : ''}`: ''}}</span>
|
||||
</template>
|
||||
</UTable>-->
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -115,6 +115,7 @@ setup()
|
||||
:type="type"
|
||||
:columns="columns"
|
||||
:rows="props.item[type]"
|
||||
style
|
||||
/>
|
||||
</UCard>
|
||||
|
||||
|
||||
@@ -68,27 +68,30 @@ setup()
|
||||
const templateColumns = [
|
||||
{
|
||||
key: "reference",
|
||||
label: "Referenz",
|
||||
sortable: true
|
||||
label: "Referenz"
|
||||
|
||||
|
||||
},
|
||||
{
|
||||
key: 'type',
|
||||
label: "Typ",
|
||||
sortable: true
|
||||
label: "Typ"
|
||||
},{
|
||||
key: 'state',
|
||||
label: "Status",
|
||||
sortable: true
|
||||
label: "Status"
|
||||
},{
|
||||
key: 'paid',
|
||||
label: "Bezahlt"
|
||||
},{
|
||||
key: 'amount',
|
||||
label: "Betrag"
|
||||
},
|
||||
{
|
||||
key: "date",
|
||||
label: "Datum",
|
||||
sortable: true
|
||||
label: "Datum"
|
||||
},
|
||||
{
|
||||
key: "dueDate",
|
||||
label: "Fällig",
|
||||
sortable: true
|
||||
label: "Fällig"
|
||||
}
|
||||
]
|
||||
const selectedColumns = ref(tempStore.columns["createddocuments"] ? tempStore.columns["createddocuments"] : templateColumns)
|
||||
@@ -119,7 +122,14 @@ const getAvailableQueryStringData = (keys) => {
|
||||
}
|
||||
|
||||
const invoiceDeliveryNotes = () => {
|
||||
router.push(`/createDocument/edit?type=invoices&linkedDocuments=[${props.item.createddocuments.filter(i => i.type === "deliveryNotes").map(i => i.id)}]`)
|
||||
router.push(`/createDocument/edit?type=invoices&loadMode=deliveryNotes&linkedDocuments=[${props.item.createddocuments.filter(i => i.type === "deliveryNotes").map(i => i.id)}]`)
|
||||
}
|
||||
|
||||
const showFinalInvoiceConfig = ref(false)
|
||||
const referenceDocument = ref(null)
|
||||
const advanceInvoicesToAdd = ref([])
|
||||
const invoiceAdvanceInvoices = () => {
|
||||
router.push(`/createDocument/edit?type=invoices&loadMode=finalInvoice&linkedDocuments=[${[referenceDocument.value, ... advanceInvoicesToAdd.value]}]`)
|
||||
}
|
||||
|
||||
const selectItem = (item) => {
|
||||
@@ -166,6 +176,59 @@ const selectItem = (item) => {
|
||||
>
|
||||
+ Abschlagsrechnung
|
||||
</UButton>
|
||||
<UButton
|
||||
@click="showFinalInvoiceConfig = true"
|
||||
v-if="props.topLevelType === 'projects'"
|
||||
:disabled="!props.item.createddocuments.filter(i => !i.archived && i.type === 'advanceInvoices').length > 0"
|
||||
>
|
||||
+ Schlussrechnung
|
||||
</UButton>
|
||||
<UModal
|
||||
prevent-close
|
||||
v-model="showFinalInvoiceConfig"
|
||||
>
|
||||
<UCard>
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
|
||||
Schlussrechnung konfigurieren
|
||||
</h3>
|
||||
<UButton color="gray" variant="ghost" icon="i-heroicons-x-mark-20-solid" class="-my-1" @click="isOpen = false" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<UFormGroup
|
||||
label="Rechnungsvorlage"
|
||||
>
|
||||
<USelectMenu
|
||||
:options="props.item.createddocuments.filter(i => ['confirmationOrders','quotes'].includes(i.type))"
|
||||
value-attribute="id"
|
||||
option-attribute="documentNumber"
|
||||
v-model="referenceDocument"
|
||||
/>
|
||||
</UFormGroup>
|
||||
|
||||
<UFormGroup
|
||||
label="Abschlagsrechnungen"
|
||||
>
|
||||
<USelectMenu
|
||||
:options="props.item.createddocuments.filter(i => ['advanceInvoices'].includes(i.type))"
|
||||
multiple
|
||||
value-attribute="id"
|
||||
option-attribute="documentNumber"
|
||||
v-model="advanceInvoicesToAdd"
|
||||
/>
|
||||
</UFormGroup>
|
||||
|
||||
<template #footer>
|
||||
<UButton
|
||||
@click="invoiceAdvanceInvoices"
|
||||
>
|
||||
Weiter
|
||||
</UButton>
|
||||
</template>
|
||||
</UCard>
|
||||
</UModal>
|
||||
<UButton
|
||||
@click="router.push(`/createDocument/edit/?${getAvailableQueryStringData({type: 'invoices'})}`)"
|
||||
>
|
||||
@@ -195,6 +258,7 @@ const selectItem = (item) => {
|
||||
:ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }"
|
||||
@select="selectItem"
|
||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Belege anzuzeigen' }"
|
||||
style="height: 70vh"
|
||||
>
|
||||
<template #type-data="{row}">
|
||||
{{dataStore.documentTypesForCreation[row.type].labelSingle}}
|
||||
@@ -219,6 +283,12 @@ const selectItem = (item) => {
|
||||
{{row.state}}
|
||||
</span>
|
||||
</template>
|
||||
<template #paid-data="{row}">
|
||||
<div v-if="(row.type === 'invoices' ||row.type === 'advanceInvoices') && row.state === 'Gebucht'">
|
||||
<span v-if="useSum().getIsPaid(row,createddocuments)" class="text-primary-500">Bezahlt</span>
|
||||
<span v-else class="text-rose-600">Offen</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #reference-data="{row}">
|
||||
<span v-if="row === props.item.createddocuments[selectedItem]" class="text-primary-500 font-bold">{{row.documentNumber}}</span>
|
||||
<span v-else>{{row.documentNumber}}</span>
|
||||
@@ -230,11 +300,12 @@ const selectItem = (item) => {
|
||||
<template #dueDate-data="{row}">
|
||||
<span v-if="row.paymentDays && ['invoices','advanceInvoices'].includes(row.type)" >{{row.documentDate ? dayjs(row.documentDate).add(row.paymentDays,'day').format("DD.MM.YY") : ''}}</span>
|
||||
</template>
|
||||
<!-- <template #amount-data="{row}">
|
||||
<template #amount-data="{row}">
|
||||
<span v-if="row.type !== 'deliveryNotes'">{{useCurrency(useSum().getCreatedDocumentSum(row, createddocuments))}}</span>
|
||||
</template>-->
|
||||
</template>
|
||||
</UTable>
|
||||
|
||||
|
||||
</UCard>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -11,7 +11,16 @@ const setup = async () => {
|
||||
data = data.filter((message) => message.profiles.length === 0)
|
||||
|
||||
globalMessages.value = data
|
||||
|
||||
if(data.length > 0) {
|
||||
messageToShow.value = data[0]
|
||||
showMessageModal.value = true
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
const showMessageModal = ref(false)
|
||||
const messageToShow = ref(null)
|
||||
|
||||
@@ -24,15 +33,32 @@ const markMessageAsRead = async () => {
|
||||
profile: profileStore.activeProfile.id,
|
||||
message: messageToShow.value.id,
|
||||
})
|
||||
setup()
|
||||
showMessageModal.value = false
|
||||
setup()
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
setup()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCard
|
||||
<UModal v-model="showMessageModal" prevent-close>
|
||||
<UCard>
|
||||
<template #header>
|
||||
<span class="font-bold">{{messageToShow.title}}</span>
|
||||
</template>
|
||||
<p class=" my-2" v-html="messageToShow.description"></p>
|
||||
<UButton
|
||||
variant="outline"
|
||||
@click="markMessageAsRead"
|
||||
>Gelesen</UButton>
|
||||
</UCard>
|
||||
</UModal>
|
||||
|
||||
<!-- <UCard
|
||||
v-if="globalMessages.length >0"
|
||||
class="mt-3"
|
||||
style="border: .75px solid #69c350"
|
||||
@@ -55,7 +81,7 @@ setup()
|
||||
>Gelesen</UButton>
|
||||
</UCard>
|
||||
</UModal>
|
||||
</UCard>
|
||||
</UCard>-->
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -125,7 +125,7 @@ const links = computed(() => {
|
||||
icon: "i-heroicons-user-group",
|
||||
children: [
|
||||
... profileStore.ownTenant.features.timeTracking ? [{
|
||||
label: "Zeiterfassung",
|
||||
label: "Projektzeiten",
|
||||
to: "/times",
|
||||
icon: "i-heroicons-clock"
|
||||
}] : [],
|
||||
|
||||
@@ -12,10 +12,42 @@ let incomeData = ref({})
|
||||
let expenseData = ref({})
|
||||
|
||||
const setup = async () => {
|
||||
let incomeRawData = (await supabase.from("createddocuments").select().eq("tenant",profileStore.currentTenant).in('type',['invoices','cancellationInvoices'])).data
|
||||
let incomeRawData = (await supabase.from("createddocuments").select().eq("tenant",profileStore.currentTenant).eq("state","Gebucht").in('type',['invoices','advanceInvoices','cancellationInvoices'])).data
|
||||
console.log(incomeRawData)
|
||||
let incomeRawFilteredData = incomeRawData.filter(x => x.state === 'Gebucht' && incomeRawData.find(i => i.linkedDocument && i.linkedDocument.id === x.id && i.type === 'cancellationInvoices') && ['invoices','advanceInvoices'].includes(row.type))
|
||||
|
||||
|
||||
let expenseRawData =(await supabase.from("incominginvoices").select().eq("tenant",profileStore.currentTenant)).data
|
||||
let withoutInvoiceRawData = (await supabase.from("statementallocations").select().eq("tenant",profileStore.currentTenant).not("account","is",null)).data
|
||||
|
||||
let withoutInvoiceRawDataExpenses = []
|
||||
let withoutInvoiceRawDataIncomes = []
|
||||
|
||||
withoutInvoiceRawData.forEach(i => {
|
||||
if(i.amount > 0) {
|
||||
withoutInvoiceRawDataIncomes.push({
|
||||
id: i.id,
|
||||
date: dayjs(i.created_at).format("DD-MM-YY"),
|
||||
amount: Math.abs(i.amount),
|
||||
bs_id: i.bs_id
|
||||
})
|
||||
} else if(i.amount < 0) {
|
||||
withoutInvoiceRawDataExpenses.push({
|
||||
id: i.id,
|
||||
date: dayjs(i.created_at).format("DD-MM-YY"),
|
||||
amount: Math.abs(i.amount),
|
||||
bs_id: i.bs_id
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
/*withoutInvoiceRawDataExpenses.forEach(i => {
|
||||
expenseData.value[i.date] ? expenseData.value[i.date] = Number((expenseData.value[i.date] + i.amount).toFixed(2)) : expenseData.value[i.date] = i.amount
|
||||
})
|
||||
|
||||
withoutInvoiceRawDataIncomes.forEach(i => {
|
||||
incomeData.value[i.date] ? incomeData.value[i.date] = Number((incomeData.value[i.date] + i.amount).toFixed(2)) : incomeData.value[i.date] = i.amount
|
||||
})*/
|
||||
|
||||
expenseRawData = expenseRawData.filter(i => i.date).map(i => {
|
||||
let amount = 0
|
||||
@@ -55,6 +87,8 @@ const setup = async () => {
|
||||
}
|
||||
Object.keys(expenseMonths).forEach(month => {
|
||||
let dates = Object.keys(expenseData.value).filter(i => i.split("-")[1] === month && i.split("-")[2] === dayjs().format("YY"))
|
||||
console.log(dates)
|
||||
|
||||
|
||||
dates.forEach(date => {
|
||||
if(expenseMonths[month]){
|
||||
|
||||
@@ -12,23 +12,29 @@ let unpaidOverdueInvoicesCount = ref(0)
|
||||
let draftInvoicesSum = ref(0)
|
||||
let draftInvoicesCount = ref(0)
|
||||
|
||||
let countUnfinishedOpenIncomingInvoices = ref(0)
|
||||
let countPreparedOpenIncomingInvoices = ref(0)
|
||||
|
||||
const setupPage = async () => {
|
||||
let documents = (await useSupabaseSelect("createddocuments","*, statementallocations(*), customer(id,name)")).filter(i => i.type === "invoices" ||i.type === "advanceInvoices"||i.type === "cancellationInvoices").filter(i => !i.archived)
|
||||
let items = (await useSupabaseSelect("createddocuments","*, statementallocations(*), customer(id,name), linkedDocument(*)")).filter(i => !i.archived)
|
||||
let documents = items.filter(i => i.type === "invoices" ||i.type === "advanceInvoices")
|
||||
|
||||
let draftDocuments = documents.filter(i => i.state === "Entwurf")
|
||||
let finalizedDocuments = documents.filter(i => i.state === "Gebucht")
|
||||
|
||||
finalizedDocuments = finalizedDocuments.filter(i => i.statementallocations.reduce((n,{amount}) => n + amount, 0).toFixed(2) !== useSum().getCreatedDocumentSum(i, documents).toFixed(2))
|
||||
|
||||
finalizedDocuments = finalizedDocuments.filter(x => (x.type === 'invoices' || x.type === 'advanceInvoices') && x.state === 'Gebucht' && !items.find(i => i.linkedDocument && i.linkedDocument.id === x.id))
|
||||
|
||||
console.log(finalizedDocuments)
|
||||
|
||||
finalizedDocuments.forEach(i => {
|
||||
console.log(i)
|
||||
//if(process.dev) console.log(i)
|
||||
//if(process.dev) console.log(useSum().getCreatedDocumentSum(i, documents) - i.statementallocations.reduce((n,{amount}) => n + amount, 0))
|
||||
if(dayjs().subtract(i.paymentDays,"days").isAfter(i.documentDate)) {
|
||||
unpaidOverdueInvoicesSum.value += useSum().getCreatedDocumentSum(i, documents) - i.statementallocations.reduce((n,{amount}) => n + amount, 0)
|
||||
unpaidOverdueInvoicesCount.value += 1
|
||||
} else {
|
||||
unpaidInvoicesSum.value += useSum().getCreatedDocumentSum(i, documents) - i.statementallocations.reduce((n,{amount}) => n + amount, 0)
|
||||
unpaidInvoicesSum.value += useSum().getCreatedDocumentSum(i, items) - i.statementallocations.reduce((n,{amount}) => n + amount, 0)
|
||||
unpaidInvoicesCount.value += 1
|
||||
}
|
||||
})
|
||||
@@ -39,8 +45,7 @@ const setupPage = async () => {
|
||||
})
|
||||
draftInvoicesCount.value = draftDocuments.length
|
||||
|
||||
let filetype = (await supabase.from("filetags").select().eq("tenant",profileStore.currentTenant).eq("incomingDocumentType","invoices").single()).data.id
|
||||
countUnfinishedOpenIncomingInvoices.value = (await supabase.from("files").select("id").eq("tenant",profileStore.currentTenant).eq("type", filetype).is("incominginvoice",null)).data.length
|
||||
countPreparedOpenIncomingInvoices.value = (await supabase.from("incominginvoices").select("id").eq("tenant",profileStore.currentTenant).eq("state", "Vorbereitet")).data.length
|
||||
|
||||
|
||||
}
|
||||
@@ -62,7 +67,7 @@ setupPage()
|
||||
<tr>
|
||||
<td class="break-all">Überfällige Rechnungen:</td>
|
||||
<td
|
||||
v-if="unpaidOverdueInvoicesSum > 0"
|
||||
v-if="unpaidOverdueInvoicesSum !== 0"
|
||||
class="text-rose-600 font-bold text-nowrap"
|
||||
>{{unpaidOverdueInvoicesCount}} Stk /<br> {{useCurrency(unpaidOverdueInvoicesSum)}}</td>
|
||||
<td v-else class="text-primary-500 font-bold text-no-wrap">0 Stk / 0,00€</td>
|
||||
@@ -78,9 +83,9 @@ setupPage()
|
||||
<tr>
|
||||
<td class="break-all">ToDo Eingangsrechnungsrechnungen:</td>
|
||||
<td
|
||||
v-if="countUnfinishedOpenIncomingInvoices > 0"
|
||||
v-if="countPreparedOpenIncomingInvoices > 0"
|
||||
class="text-orange-500 font-bold text-nowrap"
|
||||
>{{countUnfinishedOpenIncomingInvoices}} Stk </td>
|
||||
>{{countPreparedOpenIncomingInvoices}} Stk </td>
|
||||
<td v-else class="text-primary-500 font-bold text-no-wrap">0 Stk</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -43,9 +43,9 @@ const startTime = async () => {
|
||||
.select()
|
||||
if(error) {
|
||||
console.log(error)
|
||||
toast.add({title: "Fehler beim starten der Zeit",color:"rose"})
|
||||
toast.add({title: "Fehler beim starten der Projektzeit",color:"rose"})
|
||||
} else if(data) {
|
||||
toast.add({title: "Zeit erfolgreich gestartet"})
|
||||
toast.add({title: "Projektzeit erfolgreich gestartet"})
|
||||
runningTimeInfo.value = data[0]
|
||||
//console.log(runningTimeInfo.value)
|
||||
}
|
||||
@@ -63,11 +63,11 @@ const stopStartedTime = async () => {
|
||||
if(error) {
|
||||
console.log(error)
|
||||
let errorId = await useError().logError(`${status} - ${JSON.stringify(error)}`)
|
||||
toast.add({title: errorId ? `Fehler beim stoppen der Zeit (Fehler ID: ${errorId})` : `Fehler beim stoppen der Zeit`,color:"rose"})
|
||||
toast.add({title: errorId ? `Fehler beim stoppen der Projektzeit (Fehler ID: ${errorId})` : `Fehler beim stoppen der Projektzeit`,color:"rose"})
|
||||
|
||||
|
||||
} else {
|
||||
toast.add({title: "Zeit erfolgreich gestoppt"})
|
||||
toast.add({title: "Projektzeit erfolgreich gestoppt"})
|
||||
runningTimeInfo.value = {}
|
||||
}
|
||||
}
|
||||
@@ -111,7 +111,7 @@ const stopStartedTime = async () => {
|
||||
</UButton>
|
||||
</div>
|
||||
<div v-else>
|
||||
<p>Keine Zeit gestartet</p>
|
||||
<p>Keine Projektzeit gestartet</p>
|
||||
<UButton
|
||||
class="mt-3"
|
||||
@click="startTime"
|
||||
|
||||
@@ -123,8 +123,6 @@ export const useFiles = () => {
|
||||
})
|
||||
}
|
||||
|
||||
//console.log(data)
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import axios from "axios";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
const baseURL = /*"http://localhost:3333"*/ "https://functions.fedeo.io"
|
||||
const baseURL = /*"http://192.168.1.129:3333"*/ /*"http://localhost:3333"*/ "https://functions.fedeo.io"
|
||||
|
||||
export const useFunctions = () => {
|
||||
const supabase = useSupabaseClient()
|
||||
@@ -101,6 +101,26 @@ export const useFunctions = () => {
|
||||
|
||||
}
|
||||
|
||||
const useGetInvoiceData = async (file) => {
|
||||
const {data:{session:{access_token}}} = await supabase.auth.getSession()
|
||||
|
||||
const {data} = await axios({
|
||||
method: "POST",
|
||||
url: `${baseURL}/functions/getinvoicedatafromgpt`,
|
||||
data: {
|
||||
file
|
||||
},
|
||||
headers: {
|
||||
Authorization: `Bearer ${access_token}`
|
||||
}
|
||||
})
|
||||
|
||||
console.log(data)
|
||||
|
||||
return data
|
||||
|
||||
}
|
||||
|
||||
const useSendTelegramNotification = async (message) => {
|
||||
const {data:{session:{access_token}}} = await supabase.auth.getSession()
|
||||
|
||||
@@ -152,5 +172,5 @@ export const useFunctions = () => {
|
||||
|
||||
}
|
||||
|
||||
return {getWorkingTimesEvaluationData, useNextNumber, useCreateTicket, useBankingGenerateLink, useBankingCheckInstitutions, useBankingListRequisitions, useCreatePDF, useSendTelegramNotification}
|
||||
return {getWorkingTimesEvaluationData, useNextNumber, useCreateTicket, useBankingGenerateLink, useBankingCheckInstitutions, useBankingListRequisitions, useCreatePDF,useGetInvoiceData, useSendTelegramNotification}
|
||||
}
|
||||
@@ -139,6 +139,13 @@ export const useSum = () => {
|
||||
}
|
||||
}
|
||||
|
||||
return {getIncomingInvoiceSum, getCreatedDocumentSum, getCreatedDocumentSumDetailed}
|
||||
const getIsPaid = (createddocument,createddocuments) => {
|
||||
let amountPaid = 0
|
||||
createddocument.statementallocations.forEach(allocation => amountPaid += allocation.amount)
|
||||
|
||||
return Number(amountPaid.toFixed(2)) === getCreatedDocumentSum(createddocument,createddocuments)
|
||||
}
|
||||
|
||||
return {getIncomingInvoiceSum, getCreatedDocumentSum, getCreatedDocumentSumDetailed, getIsPaid}
|
||||
|
||||
}
|
||||
@@ -167,12 +167,12 @@ const footerLinks = [/*{
|
||||
variant="ghost"
|
||||
:color="route.fullPath === '/standardEntity/projects' ? 'primary' : 'gray'"
|
||||
/>
|
||||
<UButton
|
||||
<!-- <UButton
|
||||
icon="i-heroicons-clock"
|
||||
to="/workingtimes"
|
||||
variant="ghost"
|
||||
:color="route.fullPath === '/workingtimes' ? 'primary' : 'gray'"
|
||||
/>
|
||||
/>-->
|
||||
<UButton
|
||||
icon="i-heroicons-bars-4"
|
||||
to="/mobile/menu"
|
||||
|
||||
@@ -17,9 +17,6 @@ definePageMeta({
|
||||
middleware: "auth"
|
||||
})
|
||||
|
||||
const showProductSelectionModal = ref(false)
|
||||
const showServiceSelectionModal = ref(false)
|
||||
|
||||
|
||||
const itemInfo = ref({
|
||||
type: "invoices",
|
||||
@@ -107,7 +104,7 @@ const setupPage = async () => {
|
||||
checkCompatibilityWithInputPrice()
|
||||
}
|
||||
|
||||
if(itemInfo.value.project) checkForOpenAdvanceInvoices()
|
||||
if(itemInfo.value.project) await checkForOpenAdvanceInvoices()
|
||||
|
||||
if(!itemInfo.value.deliveryDateType) itemInfo.value.deliveryDateType = "Lieferdatum"
|
||||
|
||||
@@ -132,74 +129,150 @@ const setupPage = async () => {
|
||||
|
||||
if(route.query.linkedDocuments) {
|
||||
|
||||
let linkedDocuments = (await supabase.from("createddocuments").select().in("id",JSON.parse(route.query.linkedDocuments))).data
|
||||
console.log(route.query.loadMode)
|
||||
|
||||
//TODO: Implement Checking for Same Customer, Contact and Project
|
||||
if(route.query.loadMode === "deliveryNotes") {
|
||||
let linkedDocuments = (await supabase.from("createddocuments").select().in("id",JSON.parse(route.query.linkedDocuments))).data
|
||||
|
||||
itemInfo.value.customer = linkedDocuments[0].customer
|
||||
itemInfo.value.project = linkedDocuments[0].project
|
||||
itemInfo.value.contact = linkedDocuments[0].contact
|
||||
//TODO: Implement Checking for Same Customer, Contact and Project
|
||||
|
||||
setCustomerData()
|
||||
itemInfo.value.customer = linkedDocuments[0].customer
|
||||
itemInfo.value.project = linkedDocuments[0].project
|
||||
itemInfo.value.contact = linkedDocuments[0].contact
|
||||
|
||||
let firstDate = null
|
||||
let lastDate = null
|
||||
setCustomerData()
|
||||
|
||||
let firstDate = null
|
||||
let lastDate = null
|
||||
|
||||
linkedDocuments.forEach(doc => {
|
||||
let lastId = 0
|
||||
itemInfo.value.rows.forEach(row => {
|
||||
if(row.id > lastId) lastId = row.id
|
||||
})
|
||||
|
||||
if(dayjs(doc.documentDate).isBefore(firstDate) || !firstDate) firstDate = doc.documentDate
|
||||
if(dayjs(doc.documentDate).isAfter(lastDate) || !lastDate) lastDate = doc.documentDate
|
||||
|
||||
itemInfo.value.rows.push(...[
|
||||
{
|
||||
id:uuidv4(),
|
||||
mode: "title",
|
||||
text: `${doc.title} vom ${dayjs(doc.documentDate).format("DD.MM.YYYY")}`
|
||||
},
|
||||
...doc.rows
|
||||
])
|
||||
|
||||
linkedDocuments.forEach(doc => {
|
||||
let lastId = 0
|
||||
itemInfo.value.rows.forEach(row => {
|
||||
if(row.id > lastId) lastId = row.id
|
||||
})
|
||||
|
||||
if(dayjs(doc.documentDate).isBefore(firstDate) || !firstDate) firstDate = doc.documentDate
|
||||
if(dayjs(doc.documentDate).isAfter(lastDate) || !lastDate) lastDate = doc.documentDate
|
||||
itemInfo.value.deliveryDateType = "Leistungszeitraum"
|
||||
itemInfo.value.deliveryDate = firstDate
|
||||
itemInfo.value.deliveryDateEnd = lastDate
|
||||
|
||||
itemInfo.value.rows.push(...[
|
||||
{
|
||||
itemInfo.value.rows.forEach(row => {
|
||||
|
||||
row.discountPercent = 0
|
||||
|
||||
setRowData(row)
|
||||
|
||||
})
|
||||
|
||||
setPosNumbers()
|
||||
|
||||
console.log(linkedDocuments)
|
||||
|
||||
if(linkedDocuments.find(i => i.rows.find( x => x.agriculture.dieselUsage))){
|
||||
console.log("has diesel")
|
||||
|
||||
//Remove Existing Total Diesel Pos
|
||||
itemInfo.value.rows = itemInfo.value.rows.filter(i => i.key !== "dieselPos")
|
||||
//Remove Existing Total Ad Blue Pos
|
||||
itemInfo.value.rows = itemInfo.value.rows.filter(i => i.key !== "adbluePos")
|
||||
|
||||
//Add Total Title
|
||||
itemInfo.value.rows.push({
|
||||
id:uuidv4(),
|
||||
mode: "title",
|
||||
text: `${doc.title} vom ${dayjs(doc.documentDate).format("DD.MM.YYYY")}`
|
||||
},
|
||||
...doc.rows
|
||||
])
|
||||
text: "Allgemein"
|
||||
})
|
||||
|
||||
})
|
||||
processDieselPosition()
|
||||
}
|
||||
|
||||
itemInfo.value.deliveryDateType = "Leistungszeitraum"
|
||||
itemInfo.value.deliveryDate = firstDate
|
||||
itemInfo.value.deliveryDateEnd = lastDate
|
||||
} else if(route.query.loadMode === "finalInvoice") {
|
||||
let linkedDocuments = (await supabase.from("createddocuments").select().in("id",JSON.parse(route.query.linkedDocuments))).data
|
||||
|
||||
itemInfo.value.rows.forEach(row => {
|
||||
//TODO: Implement Checking for Same Customer, Contact and Project
|
||||
|
||||
row.discountPercent = 0
|
||||
console.log(linkedDocuments)
|
||||
|
||||
setRowData(row)
|
||||
itemInfo.value.customer = linkedDocuments[0].customer
|
||||
itemInfo.value.project = linkedDocuments[0].project
|
||||
itemInfo.value.contact = linkedDocuments[0].contact
|
||||
|
||||
})
|
||||
setCustomerData()
|
||||
|
||||
setPosNumbers()
|
||||
for await (const doc of linkedDocuments.filter(i => i.type === "confirmationOrders")) {
|
||||
let linkedDocument = await useSupabaseSelectSingle("createddocuments",doc.id)
|
||||
|
||||
console.log(linkedDocuments)
|
||||
itemInfo.value.rows.push({
|
||||
mode: "title",
|
||||
text: linkedDocument.title,
|
||||
})
|
||||
|
||||
if(linkedDocuments.find(i => i.rows.find( x => x.agriculture.dieselUsage))){
|
||||
console.log("has diesel")
|
||||
itemInfo.value.rows.push(...linkedDocument.rows)
|
||||
}
|
||||
|
||||
//Remove Existing Total Diesel Pos
|
||||
itemInfo.value.rows = itemInfo.value.rows.filter(i => i.key !== "dieselPos")
|
||||
//Remove Existing Total Ad Blue Pos
|
||||
itemInfo.value.rows = itemInfo.value.rows.filter(i => i.key !== "adbluePos")
|
||||
for await (const doc of linkedDocuments.filter(i => i.type === "quotes")) {
|
||||
let linkedDocument = await useSupabaseSelectSingle("createddocuments",doc.id)
|
||||
|
||||
itemInfo.value.rows.push({
|
||||
mode: "title",
|
||||
text: linkedDocument.title,
|
||||
})
|
||||
|
||||
itemInfo.value.rows.push(...linkedDocument.rows)
|
||||
}
|
||||
|
||||
//Add Total Title
|
||||
itemInfo.value.rows.push({
|
||||
id:uuidv4(),
|
||||
mode: "title",
|
||||
text: "Allgemein"
|
||||
text: "Abschlagsrechnungen",
|
||||
})
|
||||
|
||||
processDieselPosition()
|
||||
for await (const doc of linkedDocuments.filter(i => i.type === "advanceInvoices")) {
|
||||
itemInfo.value.rows.push({
|
||||
mode: "free",
|
||||
text: `Abschlagsrechnung ${doc.documentNumber}`,
|
||||
quantity: 1,
|
||||
taxPercent: 19, // TODO TAX PERCENTAGE
|
||||
discountPercent: 0,
|
||||
unit: 10,
|
||||
inputPrice: useSum().getCreatedDocumentSumDetailed(doc).totalNet *-1,
|
||||
linkedEntitys: [
|
||||
{
|
||||
type: "createddocuments",
|
||||
subtype: "advanceInvoices",
|
||||
id: doc.id
|
||||
}
|
||||
],
|
||||
editDisabled: true
|
||||
|
||||
})
|
||||
|
||||
updateCustomSurcharge()
|
||||
|
||||
}
|
||||
|
||||
setPosNumbers()
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
if(route.query.linkedDocument) {
|
||||
@@ -236,8 +309,11 @@ const setupPage = async () => {
|
||||
} else {
|
||||
// Import all
|
||||
|
||||
if(process.dev) console.log(linkedDocument)
|
||||
|
||||
itemInfo.value.taxType = linkedDocument.taxType
|
||||
itemInfo.value.customer = linkedDocument.customer
|
||||
await setCustomerData(null,true)
|
||||
itemInfo.value.letterhead = linkedDocument.letterhead
|
||||
itemInfo.value.contact = linkedDocument.contact
|
||||
itemInfo.value.deliveryDateType = linkedDocument.deliveryDateType
|
||||
@@ -256,7 +332,7 @@ const setupPage = async () => {
|
||||
itemInfo.value.endText = linkedDocument.endText
|
||||
}
|
||||
|
||||
setCustomerData(null,true)
|
||||
|
||||
|
||||
checkCompatibilityWithInputPrice()
|
||||
|
||||
@@ -265,6 +341,12 @@ const setupPage = async () => {
|
||||
row.price = row.price * -1
|
||||
})
|
||||
|
||||
itemInfo.value.documentDate = dayjs()
|
||||
|
||||
itemInfo.value.usedAdvanceInvoices = linkedDocument.usedAdvanceInvoices
|
||||
|
||||
checkForOpenAdvanceInvoices()
|
||||
|
||||
itemInfo.value.description = `Stornorechnung zu Rechnung ${linkedDocument.documentNumber} vom ${dayjs(linkedDocument.documentDate).format('DD.MM.YYYY')}`
|
||||
|
||||
itemInfo.value.type = "cancellationInvoices"
|
||||
@@ -310,11 +392,12 @@ setupPage()
|
||||
const openAdvanceInvoices = ref([])
|
||||
const checkForOpenAdvanceInvoices = async () => {
|
||||
console.log("Check for Open Advance Invoices")
|
||||
const {data,error} = await supabase.from("createddocuments").select().eq("project", itemInfo.value.project).eq("advanceInvoiceResolved", false).eq("type","advanceInvoices")
|
||||
const {data} = await supabase.from("createddocuments").select().eq("project", itemInfo.value.project).eq("advanceInvoiceResolved", false).eq("type","advanceInvoices")
|
||||
const {data: usedAdvanceInvoices} = await supabase.from("createddocuments").select().in("id", itemInfo.value.usedAdvanceInvoices)
|
||||
|
||||
console.log(data)
|
||||
|
||||
openAdvanceInvoices.value = data
|
||||
openAdvanceInvoices.value = [...data, ...usedAdvanceInvoices.filter(i => !data.find(x => x.id === i.id))]
|
||||
|
||||
}
|
||||
|
||||
@@ -487,7 +570,8 @@ const addPosition = (mode) => {
|
||||
inputPrice: 0,
|
||||
price: 0,
|
||||
taxPercent: taxPercentage,
|
||||
discountPercent: 0
|
||||
discountPercent: 0,
|
||||
linkedEntitys: []
|
||||
}
|
||||
|
||||
itemInfo.value.rows.push({...rowData, ...profileStore.ownTenant.extraModules.includes("agriculture") ? {agriculture: {}}: {}})
|
||||
@@ -501,7 +585,8 @@ const addPosition = (mode) => {
|
||||
price: 0,
|
||||
taxPercent: taxPercentage,
|
||||
discountPercent: 0,
|
||||
unit: 1
|
||||
unit: 1,
|
||||
linkedEntitys: []
|
||||
})
|
||||
} else if(mode === 'service'){
|
||||
let rowData = {
|
||||
@@ -512,7 +597,8 @@ const addPosition = (mode) => {
|
||||
price: 0,
|
||||
taxPercent: taxPercentage,
|
||||
discountPercent: 0,
|
||||
unit: 1
|
||||
unit: 1,
|
||||
linkedEntitys: []
|
||||
}
|
||||
|
||||
//Push Agriculture Holder only if Module is activated
|
||||
@@ -521,16 +607,19 @@ const addPosition = (mode) => {
|
||||
itemInfo.value.rows.push({
|
||||
id: uuidv4(),
|
||||
mode: "pagebreak",
|
||||
linkedEntitys: []
|
||||
})
|
||||
} else if(mode === "title") {
|
||||
itemInfo.value.rows.push({
|
||||
id: uuidv4(),
|
||||
mode: "title",
|
||||
linkedEntitys: []
|
||||
})
|
||||
} else if(mode === "text") {
|
||||
itemInfo.value.rows.push({
|
||||
id: uuidv4(),
|
||||
mode: "text",
|
||||
linkedEntitys: []
|
||||
})
|
||||
}
|
||||
|
||||
@@ -546,15 +635,6 @@ const removePosition = (id) => {
|
||||
|
||||
}
|
||||
|
||||
const getRowMargin = (row) => {
|
||||
if(row.mode === "normal" && row.product) {
|
||||
let purchasePrice = products.value.find(i => i.id === row.product).purchasePrice || 0
|
||||
return row.price - purchasePrice
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
const findDocumentErrors = computed(() => {
|
||||
let errors = []
|
||||
|
||||
@@ -621,7 +701,7 @@ const findDocumentErrors = computed(() => {
|
||||
}
|
||||
|
||||
|
||||
return errors.sort((a,b) => (a.type === "breaking") ? -1 : 1)
|
||||
return errors.sort((a) => (a.type === "breaking") ? -1 : 1)
|
||||
})
|
||||
|
||||
const tabItems = computed(() => {
|
||||
@@ -637,13 +717,18 @@ const tabItems = computed(() => {
|
||||
})
|
||||
|
||||
const renderCurrency = (value, currency = "€") => {
|
||||
return Number(value).toFixed(2).replace(".",",") + " €"
|
||||
//return Number(value).toFixed(2).replace(".",",") + " " + currency
|
||||
return useCurrency(value, currency)
|
||||
}
|
||||
|
||||
const documentTotal = computed(() => {
|
||||
let totalNet = 0
|
||||
let total19 = 0
|
||||
let totalNet19 = 0
|
||||
let total7 = 0
|
||||
let totalNet7 = 0
|
||||
let total0 = 0
|
||||
let totalNet0 = 0
|
||||
|
||||
itemInfo.value.rows.filter(i => !i.optional && !i.alternative).forEach(row => {
|
||||
if(!['pagebreak','title','text'].includes(row.mode)){
|
||||
@@ -652,8 +737,12 @@ const documentTotal = computed(() => {
|
||||
|
||||
if(row.taxPercent === 19) {
|
||||
total19 = total19 + Number(rowPrice * 0.19)
|
||||
totalNet19 += Number(rowPrice)
|
||||
} else if(row.taxPercent === 7) {
|
||||
total7 = total7 + Number(rowPrice * 0.07)
|
||||
totalNet7 += Number(rowPrice)
|
||||
} else if(row.taxPercent === 0) {
|
||||
totalNet0 += Number(rowPrice)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -661,17 +750,29 @@ const documentTotal = computed(() => {
|
||||
//Title Sum
|
||||
|
||||
let titleSums = {}
|
||||
let titleSumsTransfer = {}
|
||||
let lastTitle = ""
|
||||
let transferCounter = 0
|
||||
|
||||
itemInfo.value.rows.forEach(row => {
|
||||
if(row.mode === 'title'){
|
||||
titleSums[`${row.pos} - ${row.text}`] = 0
|
||||
lastTitle = `${row.pos} - ${row.text}`
|
||||
let title = `${row.pos} - ${row.text}`
|
||||
titleSums[title] = 0
|
||||
lastTitle = title
|
||||
|
||||
//Übertrag berechnen
|
||||
titleSumsTransfer[Object.keys(titleSums)[row.pos-2]] = transferCounter
|
||||
|
||||
|
||||
} else if(!['pagebreak','text'].includes(row.mode) && lastTitle !== "" && !row.optional && !row.alternative){
|
||||
titleSums[lastTitle] = Number(titleSums[lastTitle]) + Number(Number(row.quantity) * Number(row.price) * (1 - Number(row.discountPercent) /100) )
|
||||
transferCounter += Number(Number(row.quantity) * Number(row.price) * (1 - Number(row.discountPercent) /100) )
|
||||
console.log(transferCounter)
|
||||
}
|
||||
})
|
||||
|
||||
console.log(titleSumsTransfer)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -691,13 +792,33 @@ const documentTotal = computed(() => {
|
||||
|
||||
console.log(totalGrossAlreadyPaid)
|
||||
|
||||
let sumToPay = totalGross - totalGrossAlreadyPaid
|
||||
let sumToPay = 0
|
||||
|
||||
if(itemInfo.value.type === "invoices") {
|
||||
sumToPay = totalGross - totalGrossAlreadyPaid
|
||||
} else if(itemInfo.value.type === "cancellationInvoices") {
|
||||
sumToPay = totalGross + totalGrossAlreadyPaid
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return {
|
||||
titleSums: titleSums,
|
||||
titleSumsTransfer: titleSumsTransfer,
|
||||
totalNet: totalNet,
|
||||
total19: total19,
|
||||
totalNet19: totalNet19,
|
||||
total7: total7,
|
||||
totalNet7: totalNet7,
|
||||
total0: total0,
|
||||
totalNet0: totalNet0,
|
||||
totalGross: totalGross,
|
||||
totalGrossAlreadyPaid: totalGrossAlreadyPaid,
|
||||
totalSumToPay: sumToPay
|
||||
@@ -719,18 +840,15 @@ const documentReport = computed(() => {
|
||||
itemInfo.value.rows.filter(i => !i.optional && !i.alternative).forEach(row => {
|
||||
if(row.product) {
|
||||
let product = products.value.find(i => i.id === row.product)
|
||||
console.log(product.purchasePrice)
|
||||
|
||||
totalProductsPurchasePrice += product.purchasePrice * row.quantity
|
||||
|
||||
} else if(row.service) {
|
||||
let service = services.value.find(i => i.id === row.service)
|
||||
console.log(service)
|
||||
|
||||
if(service.materialComposition) {
|
||||
service.materialComposition.forEach(entry => {
|
||||
let productData = products.value.find(i => i.id === entry.product)
|
||||
console.log(productData)
|
||||
|
||||
totalProductsFromServicesPurchasePrice += productData.purchasePrice * entry.quantity * row.quantity
|
||||
})
|
||||
@@ -930,7 +1048,15 @@ const getDocumentData = () => {
|
||||
})
|
||||
}
|
||||
|
||||
let returnTitleSumsTransfer = {}
|
||||
if(Object.keys(documentTotal.value.titleSumsTransfer).length > 0) {
|
||||
Object.keys(documentTotal.value.titleSumsTransfer).forEach(key => {
|
||||
returnTitleSumsTransfer[key] = renderCurrency(documentTotal.value.titleSumsTransfer[key])
|
||||
})
|
||||
}
|
||||
|
||||
console.log(returnTitleSums)
|
||||
console.log(returnTitleSumsTransfer)
|
||||
|
||||
|
||||
const returnData = {
|
||||
@@ -1013,13 +1139,37 @@ const getDocumentData = () => {
|
||||
endText: templateEndText(generateContext(itemInfo.value, contactData)),
|
||||
startText: templateStartText(generateContext(itemInfo.value, contactData)),
|
||||
rows: rows,
|
||||
totalArray: [
|
||||
{
|
||||
label: "Nettobetrag",
|
||||
content: renderCurrency(documentTotal.value.totalNet),
|
||||
},
|
||||
... rows.find(i => i.taxPercent === 19) ? [{
|
||||
label: `zzgl. 19% USt auf ${renderCurrency(documentTotal.value.totalNet19)}`,
|
||||
content: renderCurrency(documentTotal.value.total19),
|
||||
}] : [],
|
||||
... rows.find(i => i.taxPercent === 7) ? [{
|
||||
label: `zzgl. 7% USt auf ${renderCurrency(documentTotal.value.totalNet7)}`,
|
||||
content: renderCurrency(documentTotal.value.total7),
|
||||
}]: [],
|
||||
...rows.find(i => i.taxPercent === 0) ? [{
|
||||
label: `zzgl. 0% USt auf ${renderCurrency(documentTotal.value.totalNet0)}`,
|
||||
content: renderCurrency(documentTotal.value.total0),
|
||||
}] : [],
|
||||
{
|
||||
label: "Gesamtbetrag",
|
||||
content: renderCurrency(documentTotal.value.totalGross),
|
||||
},
|
||||
],
|
||||
total: {
|
||||
totalNet: renderCurrency(documentTotal.value.totalNet),
|
||||
total19: renderCurrency(documentTotal.value.total19),
|
||||
total0: renderCurrency(documentTotal.value.total0),
|
||||
totalGross: renderCurrency(documentTotal.value.totalGross),
|
||||
totalGrossAlreadyPaid: renderCurrency(documentTotal.value.totalGrossAlreadyPaid),
|
||||
totalSumToPay: renderCurrency(documentTotal.value.totalSumToPay),
|
||||
titleSums: returnTitleSums
|
||||
titleSums: returnTitleSums,
|
||||
titleSumsTransfer: returnTitleSumsTransfer
|
||||
},
|
||||
agriculture: itemInfo.value.agriculture,
|
||||
usedAdvanceInvoices: itemInfo.value.usedAdvanceInvoices.map(i => {
|
||||
@@ -1035,7 +1185,6 @@ const getDocumentData = () => {
|
||||
const showDocument = ref(false)
|
||||
const uri = ref("")
|
||||
const generateDocument = async () => {
|
||||
const ownTenant = profileStore.ownTenant
|
||||
const path = letterheads.value.find(i => i.id === itemInfo.value.letterhead).path
|
||||
|
||||
/*const {data,error} = await supabase.functions.invoke('create_pdf',{
|
||||
@@ -1102,7 +1251,7 @@ const saveSerialInvoice = async () => {
|
||||
address: itemInfo.value.address,
|
||||
project: itemInfo.value.project,
|
||||
paymentDays: itemInfo.value.paymentDays,
|
||||
deliveryDateType: itemInfo.value.deliveryDateType,
|
||||
deliveryDateType: "Leistungszeitraum",
|
||||
createdBy: itemInfo.value.createdBy,
|
||||
title: itemInfo.value.title,
|
||||
description: itemInfo.value.description,
|
||||
@@ -1179,7 +1328,7 @@ const saveDocument = async (state,resetup = false) => {
|
||||
|
||||
let createData = {
|
||||
type: itemInfo.value.type,
|
||||
taxType: (itemInfo.value.type === "invoices" || itemInfo.value.type === "quotes") ? itemInfo.value.taxType : null,
|
||||
taxType: ['invoices','cancellationInvoices','advanceInvoices','qoutes','confirmationOrders'].includes(itemInfo.value.type) ? itemInfo.value.taxType : null,
|
||||
state: itemInfo.value.state || "Entwurf",
|
||||
customer: itemInfo.value.customer,
|
||||
contact: itemInfo.value.contact,
|
||||
@@ -1281,7 +1430,6 @@ const getTextTemplateByType = (type, pos) => {
|
||||
|
||||
const checkCompatibilityWithInputPrice = () => {
|
||||
itemInfo.value.rows.forEach(row => {
|
||||
console.log(row)
|
||||
if(!row.inputPrice) {
|
||||
row.inputPrice = row.price
|
||||
}
|
||||
@@ -1290,13 +1438,15 @@ const checkCompatibilityWithInputPrice = () => {
|
||||
|
||||
const updateCustomSurcharge = () => {
|
||||
itemInfo.value.rows.forEach(row => {
|
||||
if(!["pagebreak","title","text"].includes(row.mode)) {
|
||||
if(!["pagebreak","title","text"].includes(row.mode) /*&& !row.linkedEntitys.find(i => i.type === "createddocuments" && i.subtype === "advanceInvoices")*/) {
|
||||
//setRowData(row)
|
||||
|
||||
row.price = Number((row.inputPrice * (1 + itemInfo.value.customSurchargePercentage /100)).toFixed(2))
|
||||
|
||||
|
||||
}
|
||||
}/* else if(!["pagebreak","title","text"].includes(row.mode) /!*&& row.linkedEntitys.find(i => i.type === "createddocuments" && i.subtype === "advanceInvoices")*!/) {
|
||||
row.price = row.inputPrice
|
||||
}*/
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1493,10 +1643,10 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
||||
|
||||
<UFormGroup
|
||||
label="Steuertyp:"
|
||||
v-if="['invoices','quotes','confirmationOrders'].includes(itemInfo.type)"
|
||||
v-if="['invoices','advanceInvoices','quotes','confirmationOrders'].includes(itemInfo.type)"
|
||||
>
|
||||
<USelectMenu
|
||||
:options="[{key:'Standard', label: 'Standard'},{key:'13b UStG', label: '13b UStG'},{key:'19 UStG', label: '19 UStG Kleinunternehmer'},{key:'12.3 UStG', label: 'PV 0% USt'}]"
|
||||
:options="[{key:'Standard', label: 'Standard'},{key:'13b UStG', label: '13b UStG'},{key:'19 UStG', label: '19 UStG Kleinunternehmer'}/*,{key:'12.3 UStG', label: 'PV 0% USt'}*/]"
|
||||
v-model="itemInfo.taxType"
|
||||
value-attribute="key"
|
||||
option-attribute="label"
|
||||
@@ -1618,7 +1768,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
||||
</UFormGroup>
|
||||
<UFormGroup
|
||||
label="Ansprechpartner:"
|
||||
v-if="itemInfo.customer ? dataStore.getCustomerById(itemInfo.customer).isCompany : false "
|
||||
v-if="itemInfo.customer ? customers.find(i => i.id === itemInfo.customer).isCompany : false "
|
||||
>
|
||||
<InputGroup>
|
||||
<USelectMenu
|
||||
@@ -1637,13 +1787,13 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
||||
class="w-full"
|
||||
:disabled="!itemInfo.customer"
|
||||
>
|
||||
<span class="truncate">{{dataStore.getContactById(itemInfo.contact) ? dataStore.getContactById(itemInfo.contact).fullName : "Kein Kontakt ausgewählt"}}</span>
|
||||
<span class="truncate">{{itemInfo.contact ? contacts.find(i => i.id === itemInfo.contact).fullName : "Kein Kontakt ausgewählt"}}</span>
|
||||
|
||||
|
||||
<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>
|
||||
<template #label>
|
||||
{{dataStore.getContactById(itemInfo.contact) ? dataStore.getContactById(itemInfo.contact).fullName : "Kein Kontakt ausgewählt"}}
|
||||
{{ itemInfo.contact ? contacts.find(i => i.id === itemInfo.contact).fullName : "Kein Kontakt ausgewählt"}}
|
||||
</template>
|
||||
</USelectMenu>
|
||||
<!-- <UButton
|
||||
@@ -1655,7 +1805,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
||||
<EntityModalButtons
|
||||
type="contacts"
|
||||
:id="itemInfo.contact"
|
||||
:create-query="{customerId: itemInfo.customer}"
|
||||
:create-query="{customer: itemInfo.customer}"
|
||||
@return-data="(data) => itemInfo.contact = data.id"
|
||||
/>
|
||||
</InputGroup>
|
||||
@@ -1667,25 +1817,25 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
||||
>
|
||||
<UInput
|
||||
v-model="itemInfo.address.street"
|
||||
:placeholder="dataStore.getCustomerById(itemInfo.customer) ? dataStore.getCustomerById(itemInfo.customer).infoData.street : 'Straße + Hausnummer'"
|
||||
:placeholder="itemInfo.customer ? customers.find(i => i.id === itemInfo.customer).infoData.street : 'Straße + Hausnummer'"
|
||||
:color="itemInfo.address.street ? 'primary' : 'rose'"
|
||||
/>
|
||||
<UInput
|
||||
v-model="itemInfo.address.special"
|
||||
class="mt-3"
|
||||
:placeholder="dataStore.getCustomerById(itemInfo.customer) ? dataStore.getCustomerById(itemInfo.customer).infoData.special : 'Adresszusatz'"
|
||||
:placeholder="itemInfo.customer ? customers.find(i => i.id === itemInfo.customer).infoData.special : 'Adresszusatz'"
|
||||
/>
|
||||
<InputGroup class="mt-3">
|
||||
<UInput
|
||||
class="flex-auto"
|
||||
v-model="itemInfo.address.zip"
|
||||
:placeholder="dataStore.getCustomerById(itemInfo.customer) ? dataStore.getCustomerById(itemInfo.customer).infoData.zip : 'PLZ'"
|
||||
:placeholder="itemInfo.customer ? customers.find(i => i.id === itemInfo.customer).infoData.zip : 'PLZ'"
|
||||
:color="itemInfo.address.zip ? 'primary' : 'rose'"
|
||||
/>
|
||||
<UInput
|
||||
class="flex-auto"
|
||||
v-model="itemInfo.address.city"
|
||||
:placeholder="dataStore.getCustomerById(itemInfo.customer) ? dataStore.getCustomerById(itemInfo.customer).infoData.city : 'Ort'"
|
||||
:placeholder="itemInfo.customer ? customers.find(i => i.id === itemInfo.customer).infoData.city : 'Ort'"
|
||||
:color="itemInfo.address.city ? 'primary' : 'rose'"
|
||||
/>
|
||||
</InputGroup>
|
||||
@@ -1880,7 +2030,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
||||
@change="checkForOpenAdvanceInvoices"
|
||||
>
|
||||
<template #label>
|
||||
{{dataStore.getProjectById(itemInfo.project) ? dataStore.getProjectById(itemInfo.project).name : "Kein Projekt ausgewählt"}}
|
||||
{{itemInfo.project ? projects.find(i => i.id === itemInfo.project).name : "Kein Projekt ausgewählt"}}
|
||||
</template>
|
||||
<template #option="{option: project}">
|
||||
{{dataStore.getCustomerById(project.customer).name}} - {{project.name}}
|
||||
@@ -2060,11 +2210,12 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
||||
<UIcon
|
||||
class="handle"
|
||||
name="i-mdi-menu"
|
||||
v-if="itemInfo.type !== 'cancellationInvoices'"
|
||||
/>
|
||||
</td>
|
||||
<td
|
||||
v-if="row.mode === 'pagebreak'"
|
||||
colspan="9"
|
||||
colspan="7"
|
||||
>
|
||||
<UDivider/>
|
||||
</td>
|
||||
@@ -2090,6 +2241,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
||||
v-if="row.mode === 'free'"
|
||||
>
|
||||
<UInput
|
||||
:disabled="itemInfo.type === 'cancellationInvoices' || row.editDisabled"
|
||||
v-model="row.text"
|
||||
placeholder="Name"
|
||||
class="min-w-40"
|
||||
@@ -2101,6 +2253,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
||||
>
|
||||
<InputGroup class="w-full">
|
||||
<USelectMenu
|
||||
:disabled="itemInfo.type === 'cancellationInvoices'"
|
||||
class="w-60"
|
||||
:options="products"
|
||||
:color="row.product ? 'primary' : 'rose'"
|
||||
@@ -2170,6 +2323,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
||||
>
|
||||
<InputGroup class="w-full">
|
||||
<USelectMenu
|
||||
:disabled="itemInfo.type === 'cancellationInvoices'"
|
||||
class="w-60"
|
||||
:options="services"
|
||||
:color="row.service ? 'primary' : 'rose'"
|
||||
@@ -2236,6 +2390,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
||||
>
|
||||
<UInput
|
||||
v-model="row.quantity"
|
||||
:disabled="itemInfo.type === 'cancellationInvoices'"
|
||||
type="number"
|
||||
:step="dataStore.units.find(i => i.id === row.unit) ? dataStore.units.find(i => i.id === row.unit).step : '1' "
|
||||
|
||||
@@ -2247,6 +2402,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
||||
>
|
||||
<USelectMenu
|
||||
v-model="row.unit"
|
||||
:disabled="itemInfo.type === 'cancellationInvoices'"
|
||||
:options="dataStore.units"
|
||||
option-attribute="name"
|
||||
value-attribute="id"
|
||||
@@ -2262,6 +2418,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
||||
>
|
||||
<UInput
|
||||
v-model="row.inputPrice"
|
||||
:disabled="itemInfo.type === 'cancellationInvoices'"
|
||||
type="number"
|
||||
step="0.001"
|
||||
@change="updateCustomSurcharge"
|
||||
@@ -2325,10 +2482,12 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
||||
v-if="!['pagebreak','title','text'].includes(row.mode)"
|
||||
>
|
||||
<UButton
|
||||
:disabled="itemInfo.type === 'cancellationInvoices'"
|
||||
icon="i-heroicons-pencil-square-16-solid"
|
||||
@click="row.showEdit = true"
|
||||
/>
|
||||
<UButton
|
||||
:disabled="itemInfo.type === 'cancellationInvoices'"
|
||||
icon="i-mdi-water-drop-outline"
|
||||
class="ml-3"
|
||||
v-if="row.agriculture"
|
||||
@@ -2347,6 +2506,36 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
||||
<UButton color="gray" variant="ghost" icon="i-heroicons-x-mark-20-solid" class="-my-1" @click="row.showEdit = false" />
|
||||
</div>
|
||||
</template>
|
||||
<InputGroup>
|
||||
<UFormGroup
|
||||
label="Anzahl:"
|
||||
class="flex-auto"
|
||||
>
|
||||
<UInput
|
||||
v-model="row.quantity"
|
||||
type="number"
|
||||
:step="dataStore.units.find(i => i.id === row.unit) ? dataStore.units.find(i => i.id === row.unit).step : '1' "
|
||||
|
||||
/>
|
||||
</UFormGroup>
|
||||
<UFormGroup
|
||||
label="Einheit:"
|
||||
class="flex-auto"
|
||||
>
|
||||
<USelectMenu
|
||||
v-model="row.unit"
|
||||
:options="dataStore.units"
|
||||
option-attribute="name"
|
||||
|
||||
value-attribute="id"
|
||||
>
|
||||
<template #label>
|
||||
{{dataStore.units.find(i => i.id === row.unit) ? dataStore.units.find(i => i.id === row.unit).name : "Keine Einheit gewählt"}}
|
||||
</template>
|
||||
</USelectMenu>
|
||||
</UFormGroup>
|
||||
</InputGroup>
|
||||
|
||||
<UFormGroup
|
||||
label="Einzelpreis:"
|
||||
v-if="itemInfo.type !== 'deliveryNotes'"
|
||||
@@ -2432,6 +2621,26 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
||||
</UFormGroup>
|
||||
</InputGroup>
|
||||
|
||||
<UAlert
|
||||
v-if="row.linkedEntitys && row.linkedEntitys.find(i => i.type === 'createddocuments' && i.subtype === 'advanceInvoices')"
|
||||
title="Berechnung des Individuellen Aufschlags für die Zeile gesperrt"
|
||||
color="orange"
|
||||
variant="subtle"
|
||||
class="my-3"
|
||||
/>
|
||||
|
||||
Verknüpfungen:
|
||||
<ul>
|
||||
<li
|
||||
v-for="linkedEntity in row.linkedEntitys"
|
||||
>
|
||||
- <a
|
||||
v-if="linkedEntity.subtype === 'advanceInvoices'"
|
||||
:to="`/createddocuments/show/${linkedEntity.id}`"
|
||||
>Abschlagsrechnung</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<UFormGroup
|
||||
label="Beschreibung:"
|
||||
class="mt-3"
|
||||
@@ -2546,12 +2755,14 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
||||
colspan="6"
|
||||
>
|
||||
<UInput
|
||||
:disabled="itemInfo.type === 'cancellationInvoices'"
|
||||
v-model="row.text"
|
||||
placeholder="Titel"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<UButton
|
||||
:disabled="itemInfo.type === 'cancellationInvoices'"
|
||||
variant="ghost"
|
||||
color="rose"
|
||||
icon="i-heroicons-x-mark-16-solid"
|
||||
@@ -2574,47 +2785,50 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
||||
<UButton
|
||||
@click="addPosition('service')"
|
||||
class="mt-3"
|
||||
:disabled="itemInfo.type === 'advanceInvoices'"
|
||||
:disabled="['advanceInvoices','cancellationInvoices'].includes(itemInfo.type)"
|
||||
>
|
||||
+ Leistung
|
||||
</UButton>
|
||||
<UButton
|
||||
@click="addPosition('normal')"
|
||||
class="mt-3"
|
||||
:disabled="itemInfo.type === 'advanceInvoices'"
|
||||
:disabled="['advanceInvoices','cancellationInvoices'].includes(itemInfo.type)"
|
||||
>
|
||||
+ Artikel
|
||||
</UButton>
|
||||
<UButton
|
||||
@click="addPosition('free')"
|
||||
class="mt-3"
|
||||
:disabled="itemInfo.type === 'advanceInvoices'"
|
||||
:disabled="['advanceInvoices','cancellationInvoices'].includes(itemInfo.type)"
|
||||
>
|
||||
+ Freie Position
|
||||
</UButton>
|
||||
<UButton
|
||||
@click="addPosition('pagebreak')"
|
||||
class="mt-3"
|
||||
:disabled="['advanceInvoices','cancellationInvoices'].includes(itemInfo.type)"
|
||||
>
|
||||
+ Seitenumbruch
|
||||
</UButton>
|
||||
<UButton
|
||||
@click="addPosition('title')"
|
||||
class="mt-3"
|
||||
:disabled="['advanceInvoices','cancellationInvoices'].includes(itemInfo.type)"
|
||||
>
|
||||
+ Titel
|
||||
</UButton>
|
||||
<UButton
|
||||
@click="addPosition('text')"
|
||||
class="mt-3"
|
||||
:disabled="['advanceInvoices','cancellationInvoices'].includes(itemInfo.type)"
|
||||
>
|
||||
+ Text
|
||||
</UButton>
|
||||
</InputGroup>
|
||||
|
||||
<UDivider
|
||||
<!-- <UDivider
|
||||
class="mt-5 mb-3"
|
||||
v-if="openAdvanceInvoices.length > 0"
|
||||
v-if="openAdvanceInvoices.length > 0 || itemInfo.usedAdvanceInvoices.length > 0"
|
||||
>
|
||||
Noch nicht abgerechnete Abschlagsrechnungen
|
||||
</UDivider>
|
||||
@@ -2643,7 +2857,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
||||
X
|
||||
</UButton>
|
||||
</InputGroup>
|
||||
</div>
|
||||
</div>-->
|
||||
|
||||
<UDivider class="my-3" v-if="Object.keys(documentTotal.titleSums).length > 0">Überschriften</UDivider>
|
||||
|
||||
@@ -2693,10 +2907,18 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
||||
<td class="font-bold">Netto:</td>
|
||||
<td class="text-right">{{renderCurrency(documentTotal.totalNet)}}</td>
|
||||
</tr>
|
||||
<tr v-if="itemInfo.taxType === 'Standard'">
|
||||
<td class="font-bold">zzgl. 19 % USt:</td>
|
||||
<tr v-if="itemInfo.taxType === 'Standard' && itemInfo.rows.find(i => i.taxPercent === 19)">
|
||||
<td class="font-bold">zzgl. 19 % USt auf {{renderCurrency(documentTotal.totalNet19)}}:</td>
|
||||
<td class="text-right">{{renderCurrency(documentTotal.total19)}}</td>
|
||||
</tr>
|
||||
<tr v-if="itemInfo.taxType === 'Standard' && itemInfo.rows.find(i => i.taxPercent === 7)">
|
||||
<td class="font-bold">zzgl. 7 % USt auf {{renderCurrency(documentTotal.totalNet7)}}:</td>
|
||||
<td class="text-right">{{renderCurrency(documentTotal.total7)}}</td>
|
||||
</tr>
|
||||
<tr v-if="itemInfo.taxType === 'Standard' && itemInfo.rows.find(i => i.taxPercent === 0)">
|
||||
<td class="font-bold">zzgl. 0 % USt auf {{renderCurrency(documentTotal.totalNet0)}}:</td>
|
||||
<td class="text-right">{{renderCurrency(documentTotal.total0)}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="font-bold">Brutto:</td>
|
||||
<td class="text-right">{{renderCurrency(documentTotal.totalGross)}}</td>
|
||||
|
||||
@@ -9,11 +9,19 @@
|
||||
placeholder="Suche..."
|
||||
class="hidden lg:block"
|
||||
@keydown.esc="$event.target.blur()"
|
||||
@change="tempStore.modifySearchString('createddocuments',searchString)"
|
||||
>
|
||||
<template #trailing>
|
||||
<UKbd value="/" />
|
||||
</template>
|
||||
</UInput>
|
||||
<UButton
|
||||
icon="i-heroicons-x-mark"
|
||||
variant="outline"
|
||||
color="rose"
|
||||
@click="clearSearchString()"
|
||||
v-if="searchString.length > 0"
|
||||
/>
|
||||
<UButton
|
||||
@click="router.push(`/createDocument/edit`)"
|
||||
>
|
||||
@@ -72,62 +80,85 @@
|
||||
</template>
|
||||
</UDashboardToolbar>
|
||||
|
||||
<UTable
|
||||
:rows="filteredRows"
|
||||
:columns="columns"
|
||||
class="w-full"
|
||||
:ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }"
|
||||
@select="selectItem"
|
||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Belege anzuzeigen' }"
|
||||
>
|
||||
<template #type-data="{row}">
|
||||
{{dataStore.documentTypesForCreation[row.type].labelSingle}}
|
||||
</template>
|
||||
<template #state-data="{row}">
|
||||
<span
|
||||
v-if="row.state === 'Entwurf'"
|
||||
class="text-rose-500"
|
||||
<UTabs :items="selectedTypes" class="m-3">
|
||||
<template #item="{item}">
|
||||
<div style="height: 80vh; overflow-y: scroll">
|
||||
<UTable
|
||||
:rows="filteredRows.filter(i => item.key === 'invoices' ? ['invoices','advanceInvoices','cancellationInvoices'].includes(i.type) : item.key === i.type)"
|
||||
:columns="columns"
|
||||
class="w-full"
|
||||
:ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }"
|
||||
@select="selectItem"
|
||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Belege anzuzeigen' }"
|
||||
>
|
||||
{{row.state}}
|
||||
</span>
|
||||
<template #type-data="{row}">
|
||||
{{dataStore.documentTypesForCreation[row.type].labelSingle}}
|
||||
<span v-if="row.type === 'cancellationInvoices'"> zu {{row.linkedDocument.documentNumber}}</span>
|
||||
</template>
|
||||
<template #state-data="{row}">
|
||||
<span
|
||||
v-if="row.state === 'Gebucht'"
|
||||
class="text-cyan-500"
|
||||
v-if="row.state === 'Entwurf'"
|
||||
class="text-rose-500"
|
||||
>
|
||||
{{row.state}}
|
||||
</span>
|
||||
<!-- <span
|
||||
v-if="row.state === 'Gebucht'"
|
||||
class="text-cyan-500"
|
||||
>
|
||||
{{row.state}}
|
||||
</span>-->
|
||||
<span
|
||||
v-if="row.state === 'Gebucht' && !items.find(i => i.linkedDocument && i.linkedDocument.id === row.id)"
|
||||
class="text-primary-500"
|
||||
>
|
||||
{{row.state}}
|
||||
</span>
|
||||
<span
|
||||
v-if="row.state === 'Abgeschlossen'"
|
||||
class="text-primary-500"
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
v-else-if="row.state === 'Gebucht' && items.find(i => i.linkedDocument && i.linkedDocument.id === row.id && i.type === 'cancellationInvoices') && ['invoices','advanceInvoices'].includes(row.type)"
|
||||
class="text-cyan-500"
|
||||
>
|
||||
Storniert mit {{items.find(i => i.linkedDocument && i.linkedDocument.id === row.id).documentNumber}}
|
||||
</span>
|
||||
<span
|
||||
v-else-if="row.state === 'Gebucht'"
|
||||
class="text-primary-500"
|
||||
>
|
||||
{{row.state}}
|
||||
</span>
|
||||
</template>
|
||||
<template #partner-data="{row}">
|
||||
<span v-if="row.customer">{{row.customer ? row.customer.name : ""}}</span>
|
||||
</span>
|
||||
</template>
|
||||
<template #partner-data="{row}">
|
||||
<span v-if="row.customer">{{row.customer ? row.customer.name : ""}}</span>
|
||||
|
||||
</template>
|
||||
<template #reference-data="{row}">
|
||||
<span v-if="row === filteredRows[selectedItem]" class="text-primary-500 font-bold">{{row.documentNumber}}</span>
|
||||
<span v-else>{{row.documentNumber}}</span>
|
||||
</template>
|
||||
<template #date-data="{row}">
|
||||
<span v-if="row.date">{{row.date ? dayjs(row.date).format("DD.MM.YY") : ''}}</span>
|
||||
<span v-if="row.documentDate">{{row.documentDate ? dayjs(row.documentDate).format("DD.MM.YY") : ''}}</span>
|
||||
</template>
|
||||
<template #dueDate-data="{row}">
|
||||
<span v-if="row.state === 'Gebucht' && row.paymentDays && ['invoices','advanceInvoices'].includes(row.type) && !items.find(i => i.linkedDocument && i.linkedDocument.id === row.id)" :class="dayjs(row.documentDate).add(row.paymentDays,'day').diff(dayjs()) <= 0 && !isPaid(row) ? ['text-rose-500'] : '' ">{{row.documentDate ? dayjs(row.documentDate).add(row.paymentDays,'day').format("DD.MM.YY") : ''}}</span>
|
||||
</template>
|
||||
<template #paid-data="{row}">
|
||||
<div v-if="(row.type === 'invoices' ||row.type === 'advanceInvoices') && row.state === 'Gebucht' && !items.find(i => i.linkedDocument && i.linkedDocument.id === row.id)">
|
||||
<span v-if="useSum().getIsPaid(row,items)" class="text-primary-500">Bezahlt</span>
|
||||
<span v-else class="text-rose-600">Offen</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #amount-data="{row}">
|
||||
<span v-if="row.type !== 'deliveryNotes'">{{displayCurrency(useSum().getCreatedDocumentSum(row,items))}}</span>
|
||||
</template>
|
||||
<template #amountOpen-data="{row}">
|
||||
<span v-if="!['deliveryNotes','cancellationInvoices','quotes','confirmationOrders'].includes(row.type) && row.state !== 'Entwurf' && !useSum().getIsPaid(row,items) && !items.find(i => i.linkedDocument && i.linkedDocument.id === row.id) ">{{displayCurrency(useSum().getCreatedDocumentSum(row, items) - row.statementallocations.reduce((n,{amount}) => n + amount, 0))}}</span>
|
||||
</template>
|
||||
</UTable>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<template #reference-data="{row}">
|
||||
<span v-if="row === filteredRows[selectedItem]" class="text-primary-500 font-bold">{{row.documentNumber}}</span>
|
||||
<span v-else>{{row.documentNumber}}</span>
|
||||
</template>
|
||||
<template #date-data="{row}">
|
||||
<span v-if="row.date">{{row.date ? dayjs(row.date).format("DD.MM.YY") : ''}}</span>
|
||||
<span v-if="row.documentDate">{{row.documentDate ? dayjs(row.documentDate).format("DD.MM.YY") : ''}}</span>
|
||||
</template>
|
||||
<template #dueDate-data="{row}">
|
||||
<span v-if="row.state === 'Gebucht' && row.paymentDays && ['invoices','advanceInvoices'].includes(row.type)" :class="dayjs(row.documentDate).add(row.paymentDays,'day').diff(dayjs()) <= 0 && !isPaid(row) ? ['text-rose-500'] : '' ">{{row.documentDate ? dayjs(row.documentDate).add(row.paymentDays,'day').format("DD.MM.YY") : ''}}</span>
|
||||
</template>
|
||||
<template #paid-data="{row}">
|
||||
<div v-if="(row.type === 'invoices' ||row.type === 'advanceInvoices') && row.state === 'Gebucht'">
|
||||
<span v-if="isPaid(row)" class="text-primary-500">Bezahlt</span>
|
||||
<span v-else class="text-rose-600">Offen</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #amount-data="{row}">
|
||||
<span v-if="row.type !== 'deliveryNotes'">{{displayCurrency(useSum().getCreatedDocumentSum(row,items))}}</span>
|
||||
</template>
|
||||
</UTable>
|
||||
</UTabs>
|
||||
|
||||
</template>
|
||||
|
||||
@@ -177,7 +208,7 @@ const items = ref([])
|
||||
const selectedItem = ref(0)
|
||||
|
||||
const setupPage = async () => {
|
||||
items.value = (await useSupabaseSelect("createddocuments","*, customer(id,name), statementallocations(id,amount)","documentNumber")).filter(i => !i.archived)
|
||||
items.value = (await useSupabaseSelect("createddocuments","*, customer(id,name), statementallocations(id,amount),linkedDocument(*)","documentNumber")).filter(i => !i.archived)
|
||||
}
|
||||
|
||||
setupPage()
|
||||
@@ -212,6 +243,11 @@ const templateColumns = [
|
||||
label: "Datum",
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "amountOpen",
|
||||
label: "Offener Betrag",
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "paid",
|
||||
label: "Bezahlt",
|
||||
@@ -230,13 +266,13 @@ const templateTypes = [
|
||||
{
|
||||
key: "invoices",
|
||||
label: "Rechnungen"
|
||||
},{
|
||||
}/*,{
|
||||
key: "cancellationInvoices",
|
||||
label: "Stornorechnungen"
|
||||
},{
|
||||
key: "advanceInvoices",
|
||||
label: "Abschlagsrechnungen"
|
||||
}, {
|
||||
}*/, {
|
||||
key: "quotes",
|
||||
label: "Angebote"
|
||||
}, {
|
||||
@@ -267,11 +303,16 @@ const displayCurrency = (value, currency = "€") => {
|
||||
}
|
||||
|
||||
|
||||
const searchString = ref('')
|
||||
const searchString = ref(tempStore.searchStrings['createddocuments'] ||'')
|
||||
|
||||
const clearSearchString = () => {
|
||||
tempStore.clearSearchString('createddocuments')
|
||||
searchString.value = ''
|
||||
}
|
||||
const selectedFilters = ref([])
|
||||
const filteredRows = computed(() => {
|
||||
|
||||
let temp = items.value.filter(i => types.value.find(x => x.key === i.type))
|
||||
let temp = 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")
|
||||
|
||||
/*if(showDrafts.value === true) {
|
||||
@@ -289,17 +330,6 @@ const filteredRows = computed(() => {
|
||||
|
||||
})
|
||||
|
||||
const calculateDocSum = (row) => {
|
||||
let sum = 0
|
||||
|
||||
row.rows.forEach(row => {
|
||||
if(row.mode === "normal" || row.mode === "service" || row.mode === "free") {
|
||||
sum += row.quantity * row.price * (1 - row.discountPercent / 100) * (1 + (row.taxPercent || 0) / 100)
|
||||
}
|
||||
})
|
||||
|
||||
return sum.toFixed(2)
|
||||
}
|
||||
|
||||
const isPaid = (item) => {
|
||||
let amountPaid = 0
|
||||
|
||||
@@ -3,6 +3,19 @@
|
||||
<template>
|
||||
<UDashboardNavbar title="Serienrechnungen" :badge="filteredRows.length">
|
||||
<template #right>
|
||||
<UInput
|
||||
id="searchinput"
|
||||
v-model="searchString"
|
||||
icon="i-heroicons-funnel"
|
||||
autocomplete="off"
|
||||
placeholder="Suche..."
|
||||
class="hidden lg:block"
|
||||
@keydown.esc="$event.target.blur()"
|
||||
>
|
||||
<template #trailing>
|
||||
<UKbd value="/" />
|
||||
</template>
|
||||
</UInput>
|
||||
<UButton
|
||||
@click="router.push(`/createDocument/edit?type=serialInvoices`)"
|
||||
>
|
||||
|
||||
@@ -189,7 +189,11 @@ const sendEmail = async () => {
|
||||
</template>
|
||||
|
||||
</UDashboardNavbar>
|
||||
<UDashboardToolbar>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="scrollContainer mt-3">
|
||||
<div class="flex-col flex w-full">
|
||||
<UFormGroup
|
||||
label="Absender"
|
||||
@@ -203,7 +207,7 @@ const sendEmail = async () => {
|
||||
</UFormGroup>
|
||||
<UDivider class="my-3"/>
|
||||
<UFormGroup
|
||||
label="Empfänger"
|
||||
label="Empfänger"
|
||||
>
|
||||
<UInput
|
||||
class="w-full my-1"
|
||||
@@ -211,7 +215,7 @@ const sendEmail = async () => {
|
||||
/>
|
||||
</UFormGroup>
|
||||
<UFormGroup
|
||||
label="Kopie"
|
||||
label="Kopie"
|
||||
>
|
||||
<UInput
|
||||
class="w-full my-1"
|
||||
@@ -219,7 +223,7 @@ const sendEmail = async () => {
|
||||
/>
|
||||
</UFormGroup>
|
||||
<UFormGroup
|
||||
label="Blindkopie"
|
||||
label="Blindkopie"
|
||||
>
|
||||
<UInput
|
||||
class="w-full my-1"
|
||||
@@ -228,7 +232,7 @@ const sendEmail = async () => {
|
||||
/>
|
||||
</UFormGroup>
|
||||
<UFormGroup
|
||||
label="Betreff"
|
||||
label="Betreff"
|
||||
>
|
||||
<UInput
|
||||
class="w-full my-1"
|
||||
@@ -236,11 +240,8 @@ const sendEmail = async () => {
|
||||
/>
|
||||
</UFormGroup>
|
||||
</div>
|
||||
|
||||
</UDashboardToolbar>
|
||||
|
||||
<UDashboardPanelContent>
|
||||
<div id="parentAttachments" class="flex flex-col justify-center">
|
||||
<UDivider class="my-3"/>
|
||||
<div id="parentAttachments" class="flex flex-col justify-center mt-3">
|
||||
<span class="font-medium mb-2 text-xl">Anhänge</span>
|
||||
<!-- <UIcon
|
||||
name="i-heroicons-paper-clip"
|
||||
@@ -273,7 +274,7 @@ const sendEmail = async () => {
|
||||
@updateContent="contentChanged"
|
||||
:preloadedContent="preloadedContent"
|
||||
/>
|
||||
</UDashboardPanelContent>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -320,4 +321,13 @@ const sendEmail = async () => {
|
||||
padding: .5rem;
|
||||
}
|
||||
|
||||
.scrollContainer {
|
||||
overflow-y: scroll;
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
height: 90vh;
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -132,12 +132,12 @@ const totalCalculated = computed(() => {
|
||||
})
|
||||
|
||||
const createIncomingInvoice = async () => {
|
||||
const data = await dataStore.createNewItem('incominginvoices',itemInfo.value)
|
||||
|
||||
console.log(data)
|
||||
const data = await dataStore.createNewItem('incominginvoices',itemInfo.value,true)
|
||||
|
||||
const {error} = await supabase.from("files").update({incominginvoice: data.id}).eq("id",loadedFile.value.id)
|
||||
|
||||
router.push(`/incominginvoices/show/${data.id}`)
|
||||
|
||||
}
|
||||
|
||||
const setCostCentre = async (item,data) => {
|
||||
@@ -145,6 +145,127 @@ const setCostCentre = async (item,data) => {
|
||||
item.costCentre = data.id
|
||||
}
|
||||
|
||||
const gptLoading = ref(false)
|
||||
|
||||
const getInvoiceData = async () => {
|
||||
gptLoading.value = true
|
||||
console.log(loadedFile.value)
|
||||
|
||||
|
||||
//loadedFile.value.url
|
||||
|
||||
|
||||
/*let data = {
|
||||
"invoice_number": "3423478673",
|
||||
"invoice_date": "2025-05-30",
|
||||
"invoice_type": "incoming",
|
||||
"delivery_type": "null",
|
||||
"delivery_note_number": "null",
|
||||
"reference": "null",
|
||||
"issuer": {
|
||||
"name": "Boels Rental Germany GmbH",
|
||||
"address": "Emeranstraße 49-51, 85622 Feldkirchen, Deutschland",
|
||||
"phone": "+49-(0)1801663225",
|
||||
"email": "fakturierung@boels.de",
|
||||
"bank": "ABN AMRO Bank N.V.",
|
||||
"bic": "ABNANL2A",
|
||||
"iban": "NL09 ABNA 0520 5585 61"
|
||||
},
|
||||
"recipient": {
|
||||
"name": "Federspiel Technology UG",
|
||||
"address": "Am Schwarzen Brack 14, 26452 Sande, Deutschland",
|
||||
"phone": "null",
|
||||
"email": "null"
|
||||
},
|
||||
"invoice_items": [
|
||||
{
|
||||
"description": "Bautrockner 50 ltr.",
|
||||
"unit": "piece",
|
||||
"quantity": 1,
|
||||
"total": 395.22
|
||||
},
|
||||
{
|
||||
"description": "Servicepauschale Kat. A",
|
||||
"unit": "piece",
|
||||
"quantity": 1,
|
||||
"total": 32.1
|
||||
},
|
||||
{
|
||||
"description": "Haftungsbegrenzung A: (Schäden, exkl. Feuer/Diebstahl/Einbruch)",
|
||||
"unit": "piece",
|
||||
"quantity": 1,
|
||||
"total": 3.2
|
||||
},
|
||||
{
|
||||
"description": "Haftungsbegrenzung B: (Feuer/Diebstahl/Einbruch)",
|
||||
"unit": "piece",
|
||||
"quantity": 1,
|
||||
"total": 16.93
|
||||
}
|
||||
],
|
||||
"subtotal": 89.1,
|
||||
"tax_rate": 19,
|
||||
"tax": 16.93,
|
||||
"total": 106.03,
|
||||
"terms": "Dieser Betrag wird automatisch mittels Lastschrift von ihrem Konto eingezogen"
|
||||
}
|
||||
|
||||
console.log(data)
|
||||
console.log(data.subtotal)*/
|
||||
|
||||
|
||||
|
||||
let data = await useFunctions().useGetInvoiceData(loadedFile.value)
|
||||
|
||||
|
||||
|
||||
|
||||
if(data.invoice_number) itemInfo.value.reference = data.invoice_number
|
||||
if(data.invoice_date) itemInfo.value.date = dayjs(data.invoice_date)
|
||||
if(data.issuer.id) itemInfo.value.vendor = data.issuer.id
|
||||
if(data.invoice_duedate) itemInfo.value.dueDate = dayjs(data.invoice_duedate)
|
||||
if(data.terms) itemInfo.value.paymentType = data.terms
|
||||
if(data.subtotal) {
|
||||
itemInfo.value.accounts = [
|
||||
{
|
||||
account: null,
|
||||
amountNet: data.subtotal,
|
||||
amountTax: data.tax,
|
||||
taxType: String(data.tax_rate),
|
||||
costCentre: null,
|
||||
amountGross: Number(data.subtotal) + Number(data.tax)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
if(data.terms === "Direct Debit") {
|
||||
itemInfo.value.paymentType = "Einzug"
|
||||
} else if(data.terms === "Transfer") {
|
||||
itemInfo.value.paymentType = "Überweisung"
|
||||
} else if(data.terms === "Credit Card") {
|
||||
itemInfo.value.paymentType = "Kreditkarte"
|
||||
} else if(data.terms === "Other") {
|
||||
itemInfo.value.paymentType = "Sonstiges"
|
||||
}
|
||||
|
||||
let description = ""
|
||||
|
||||
if(data.delivery_note_number) description += `Lieferschein: ${data.delivery_note_number} \n`
|
||||
if(data.reference) description += `Referenz: ${data.reference} \n`
|
||||
if(data.invoice_items) {
|
||||
data.invoice_items.forEach(item => {
|
||||
description += `${item.description} - ${item.quantity} ${item.unit} - ${item.total}\n`
|
||||
})
|
||||
}
|
||||
itemInfo.value.description = description
|
||||
|
||||
|
||||
|
||||
|
||||
gptLoading.value = false
|
||||
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
@@ -204,6 +325,17 @@ const setCostCentre = async (item,data) => {
|
||||
</div>
|
||||
|
||||
<div v-else class=" scrollContainer">
|
||||
<UButton
|
||||
icon="i-heroicons-sparkles"
|
||||
class="my-3"
|
||||
variant="outline"
|
||||
@click="getInvoiceData"
|
||||
:disabled="gptLoading"
|
||||
>
|
||||
KI - Vorschlag
|
||||
<UProgress v-if="gptLoading" animation="carousel"/>
|
||||
</UButton>
|
||||
|
||||
<InputGroup class="mb-3">
|
||||
<UButton
|
||||
:variant="itemInfo.expense ? 'solid' : 'outline'"
|
||||
@@ -431,7 +563,7 @@ const setCostCentre = async (item,data) => {
|
||||
:color="!item.amountNet ? 'rose' : 'primary'"
|
||||
:disabled="item.taxType === null"
|
||||
@keyup="item.amountTax = Number((item.amountNet * (Number(taxOptions.find(i => i.key === item.taxType).percentage)/100)).toFixed(2)),
|
||||
item.amountGross = Number(item.amountNet) + NUmber(item.amountTax)"
|
||||
item.amountGross = Number(item.amountNet) + Number(item.amountTax)"
|
||||
>
|
||||
<template #trailing>
|
||||
<span class="text-gray-500 dark:text-gray-400 text-xs">EUR</span>
|
||||
|
||||
@@ -125,14 +125,47 @@ const totalCalculated = computed(() => {
|
||||
|
||||
})
|
||||
|
||||
const updateIncomingInvoice = async () => {
|
||||
const updateIncomingInvoice = async (setBooked = false) => {
|
||||
|
||||
let item = itemInfo.value
|
||||
delete item.files
|
||||
|
||||
if(item.state === "Vorbereitet" && !setBooked) {
|
||||
item.state = "Entwurf"
|
||||
} else if(item.state === "Vorbereitet" && setBooked) {
|
||||
item.state = "Gebucht"
|
||||
} else if(item.state === "Entwurf" && setBooked) {
|
||||
item.state = "Gebucht"
|
||||
} else {
|
||||
item.state = "Entwurf"
|
||||
}
|
||||
const data = await dataStore.updateItem('incominginvoices',item)
|
||||
}
|
||||
|
||||
const findIncomingInvoiceErrors = computed(() => {
|
||||
let errors = []
|
||||
|
||||
if(itemInfo.value.vendor === null) errors.push({message: "Es ist kein Lieferant ausgewählt", type: "breaking"})
|
||||
if(itemInfo.value.reference === null) errors.push({message: "Es ist keine Referenz angegeben", type: "breaking"})
|
||||
if(itemInfo.value.date === null) errors.push({message: "Es ist kein Datum ausgewählt", type: "breaking"})
|
||||
if(itemInfo.value.dueDate === null) errors.push({message: "Es ist kein Fälligkeitsdatum ausgewählt", type: "breaking"})
|
||||
if(itemInfo.value.paymentType === null) errors.push({message: "Es ist keine Zahlart ausgewählt", type: "breaking"})
|
||||
if(itemInfo.value.description === null) errors.push({message: "Es ist keine Beschreibung angegeben", type: "info"})
|
||||
|
||||
|
||||
itemInfo.value.accounts.forEach(account => {
|
||||
if(account.account === null) errors.push({message: "Es ist keine Kategorie ausgewählt", type: "breaking"})
|
||||
if(account.amountNet === null) errors.push({message: "Es ist kein Nettobetrag angegeben", type: "breaking"})
|
||||
if(account.taxType === null) errors.push({message: "Es ist kein Steuertyp ausgewählt", type: "breaking"})
|
||||
if(account.costCentre === null) errors.push({message: "Es ist keine Kostenstelle ausgewählt", type: "info"})
|
||||
if(account.taxType === null || account.taxType === "0") errors.push({message: "Es ist keine Steuerart ausgewählt", type: "breaking"})
|
||||
|
||||
})
|
||||
|
||||
|
||||
return errors.sort((a,b) => (a.type === "breaking") ? -1 : 1)
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
@@ -140,10 +173,16 @@ const updateIncomingInvoice = async () => {
|
||||
<UDashboardNavbar :title="'Eingangsbeleg erstellen'">
|
||||
<template #right>
|
||||
<UButton
|
||||
@click="updateIncomingInvoice"
|
||||
@click="updateIncomingInvoice(false)"
|
||||
>
|
||||
Speichern
|
||||
</UButton>
|
||||
<UButton
|
||||
@click="updateIncomingInvoice(true)"
|
||||
:disabled="findIncomingInvoiceErrors.filter(i => i.type === 'breaking').length > 0"
|
||||
>
|
||||
Speichern & Buchen
|
||||
</UButton>
|
||||
</template>
|
||||
</UDashboardNavbar>
|
||||
<UDashboardPanelContent>
|
||||
@@ -158,7 +197,22 @@ const updateIncomingInvoice = async () => {
|
||||
/>
|
||||
<div class="w-3/5 mx-5">
|
||||
|
||||
<UAlert
|
||||
class="mb-5"
|
||||
title="Vorhandene Probleme und Informationen:"
|
||||
:color="findIncomingInvoiceErrors.filter(i => i.type === 'breaking').length > 0 ? 'rose' : 'white'"
|
||||
variant="outline"
|
||||
v-if="findIncomingInvoiceErrors.length > 0"
|
||||
>
|
||||
<template #description>
|
||||
<ul class="list-disc ml-5">
|
||||
<li v-for="error in findIncomingInvoiceErrors" :class="[...error.type === 'breaking' ? ['text-rose-600'] : ['dark:text-white','text-black']]">
|
||||
{{error.message}}
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
</UAlert>
|
||||
|
||||
<div class=" scrollContainer">
|
||||
<InputGroup class="mb-3">
|
||||
@@ -196,11 +250,17 @@ const updateIncomingInvoice = async () => {
|
||||
{{dataStore.vendors.find(vendor => vendor.id === itemInfo.vendor) ? dataStore.vendors.find(vendor => vendor.id === itemInfo.vendor).name : 'Lieferant auswählen'}}
|
||||
</template>
|
||||
</USelectMenu>
|
||||
<EntityModalButtons
|
||||
type="vendors"
|
||||
:id="itemInfo.vendor"
|
||||
@return-data="(data) => itemInfo.vendor = data.id"
|
||||
/>
|
||||
<UButton
|
||||
@click="router.push('/standardEntity/vendors/create')"
|
||||
>
|
||||
+ Lieferant
|
||||
</UButton>
|
||||
icon="i-heroicons-x-mark"
|
||||
variant="outline"
|
||||
color="rose"
|
||||
@click="itemInfo.vendor = null"
|
||||
/>
|
||||
</InputGroup>
|
||||
|
||||
|
||||
@@ -304,7 +364,6 @@ const updateIncomingInvoice = async () => {
|
||||
class="my-3"
|
||||
v-for="(item,index) in itemInfo.accounts"
|
||||
>
|
||||
|
||||
<UFormGroup
|
||||
label="Kategorie"
|
||||
class="mb-3"
|
||||
@@ -360,6 +419,24 @@ const updateIncomingInvoice = async () => {
|
||||
/>
|
||||
</InputGroup>
|
||||
</UFormGroup>
|
||||
<UFormGroup
|
||||
label="Beschreibung"
|
||||
class="w-full mb-3"
|
||||
>
|
||||
<InputGroup class="w-full">
|
||||
<UInput
|
||||
v-model="item.description"
|
||||
class="flex-auto"
|
||||
></UInput>
|
||||
<UButton
|
||||
variant="outline"
|
||||
color="rose"
|
||||
v-if="item.description"
|
||||
icon="i-heroicons-x-mark"
|
||||
@click="item.description = null"
|
||||
/>
|
||||
</InputGroup>
|
||||
</UFormGroup>
|
||||
|
||||
|
||||
|
||||
@@ -470,7 +547,7 @@ const updateIncomingInvoice = async () => {
|
||||
overflow-y: scroll;
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
height: 75vh;
|
||||
height: 70vh;
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ defineShortcuts({
|
||||
})
|
||||
|
||||
const dataStore = useDataStore()
|
||||
const tempStore = useTempStore()
|
||||
const router = useRouter()
|
||||
|
||||
const sum = useSum()
|
||||
@@ -45,7 +46,7 @@ const items = ref([])
|
||||
const selectedItem = ref(0)
|
||||
|
||||
const setupPage = async () => {
|
||||
items.value = await useSupabaseSelect("incominginvoices","*, vendor(id,name), statementallocations(id,amount)")
|
||||
items.value = await useSupabaseSelect("incominginvoices","*, vendor(id,name), statementallocations(id,amount)","created_at",false)
|
||||
}
|
||||
|
||||
setupPage()
|
||||
@@ -91,9 +92,17 @@ const selectedColumns = ref(templateColumns)
|
||||
const columns = computed(() => templateColumns.filter((column) => selectedColumns.value.includes(column)))
|
||||
|
||||
|
||||
const searchString = ref('')
|
||||
const searchString = ref(tempStore.searchStrings['incominginvoices'] ||'')
|
||||
|
||||
const clearSearchString = () => {
|
||||
tempStore.clearSearchString('incominginvoices')
|
||||
searchString.value = ''
|
||||
}
|
||||
const filteredRows = computed(() => {
|
||||
return useSearch(searchString.value, items.value)
|
||||
let filteredItems = useSearch(searchString.value, items.value)
|
||||
|
||||
return [...filteredItems.filter(i => i.state === "Vorbereitet"), ...filteredItems.filter(i => i.state !== "Vorbereitet")]
|
||||
|
||||
|
||||
})
|
||||
|
||||
@@ -115,6 +124,16 @@ const isPaid = (item) => {
|
||||
return Math.abs(amountPaid) === Math.abs(Number(getInvoiceSum(item)))
|
||||
}
|
||||
|
||||
const selectIncomingInvoice = (invoice) => {
|
||||
if(invoice.state === "Vorbereitet") {
|
||||
router.push(`/incomingInvoices/edit/${invoice.id}`)
|
||||
} else {
|
||||
router.push(`/incomingInvoices/show/${invoice.id}`)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
@@ -130,13 +149,21 @@ const isPaid = (item) => {
|
||||
placeholder="Suche..."
|
||||
class="hidden lg:block"
|
||||
@keydown.esc="$event.target.blur()"
|
||||
@change="tempStore.modifySearchString('incominginvoices',searchString)"
|
||||
>
|
||||
<template #trailing>
|
||||
<UKbd value="/" />
|
||||
</template>
|
||||
</UInput>
|
||||
<UButton
|
||||
icon="i-heroicons-x-mark"
|
||||
variant="outline"
|
||||
color="rose"
|
||||
@click="clearSearchString()"
|
||||
v-if="searchString.length > 0"
|
||||
/>
|
||||
|
||||
<UButton @click="router.push(`/incomingInvoices/create`)">+ Beleg</UButton>
|
||||
<!-- <UButton @click="router.push(`/incomingInvoices/create`)">+ Beleg</UButton>-->
|
||||
</template>
|
||||
</UDashboardNavbar>
|
||||
<UDashboardToolbar>
|
||||
@@ -162,13 +189,18 @@ const isPaid = (item) => {
|
||||
:columns="columns"
|
||||
class="w-full"
|
||||
:ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }"
|
||||
@select="(i) => router.push(`/incomingInvoices/show/${i.id}`) "
|
||||
@select="(i) => selectIncomingInvoice(i) "
|
||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Belege anzuzeigen' }"
|
||||
>
|
||||
<template #reference-data="{row}">
|
||||
<span v-if="row === filteredRows[selectedItem]" class="text-primary-500 font-bold">{{row.reference}}</span>
|
||||
<span v-else>{{row.reference}}</span>
|
||||
</template>
|
||||
<template #state-data="{row}">
|
||||
<span v-if="row.state === 'Vorbereitet'" class="text-cyan-500">{{row.state}}</span>
|
||||
<span v-else-if="row.state === 'Entwurf'" class="text-red-500">{{row.state}}</span>
|
||||
<span v-else-if="row.state === 'Gebucht'" class="text-primary-500">{{row.state}}</span>
|
||||
</template>
|
||||
<template #date-data="{row}">
|
||||
{{dayjs(row.date).format("DD.MM.YYYY")}}
|
||||
</template>
|
||||
@@ -179,7 +211,7 @@ const isPaid = (item) => {
|
||||
{{displayCurrency(sum.getIncomingInvoiceSum(row))}}
|
||||
</template>
|
||||
<template #dueDate-data="{row}">
|
||||
{{dayjs(row.dueDate).format("DD.MM.YYYY")}}
|
||||
<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>
|
||||
|
||||
@@ -46,6 +46,7 @@ const loading = ref(true)
|
||||
const setupPage = async () => {
|
||||
if((mode.value === "show") && route.params.id){
|
||||
itemInfo.value = await useSupabaseSelectSingle("incominginvoices",route.params.id,"*, files(*), vendor(*)")
|
||||
if(process.dev) console.log(itemInfo.value)
|
||||
currentDocument.value = await useFiles().selectDocument(itemInfo.value.files[0].id)
|
||||
}
|
||||
loading.value = false
|
||||
@@ -70,6 +71,15 @@ setupPage()
|
||||
|
||||
<template>
|
||||
<UDashboardNavbar :title="'Eingangsbeleg anzeigen'">
|
||||
<template #left>
|
||||
<UButton
|
||||
to="/incominginvoices"
|
||||
icon="i-heroicons-chevron-left"
|
||||
variant="outline"
|
||||
>
|
||||
Übersicht
|
||||
</UButton>
|
||||
</template>
|
||||
<template #right>
|
||||
<UButton
|
||||
@click="router.push(`/incomingInvoices/edit/${itemInfo.id}`)"
|
||||
|
||||
@@ -39,15 +39,17 @@
|
||||
<display-projects-in-phases/>
|
||||
</UDashboardCard>
|
||||
<UDashboardCard
|
||||
title="Anwesenheiten"
|
||||
title="Anwesende"
|
||||
>
|
||||
<display-present-profiles/>
|
||||
</UDashboardCard>
|
||||
<UDashboardCard
|
||||
title="Projektzeiten"
|
||||
>
|
||||
<display-running-time/>
|
||||
</UDashboardCard>
|
||||
<UDashboardCard
|
||||
title="Anwesenheiten"
|
||||
>
|
||||
<display-running-working-time/>
|
||||
</UDashboardCard>
|
||||
|
||||
@@ -10,32 +10,58 @@ const profileStore = useProfileStore()
|
||||
|
||||
<template>
|
||||
<UDashboardPanelContent>
|
||||
|
||||
<UDivider class="mb-3">Weiteres</UDivider>
|
||||
<UButton
|
||||
class="w-full my-1"
|
||||
to="/times"
|
||||
icon="i-heroicons-clock"
|
||||
>
|
||||
Zeiten
|
||||
</UButton>
|
||||
<UButton
|
||||
class="w-full my-1"
|
||||
to="/standardEntity/absencerequests"
|
||||
icon="i-heroicons-document-text"
|
||||
>
|
||||
Abwesenheiten
|
||||
</UButton>
|
||||
|
||||
<UButton
|
||||
class="w-full my-1"
|
||||
to="/workingtimes"
|
||||
icon="i-heroicons-clock"
|
||||
>
|
||||
Anwesenheiten
|
||||
</UButton>
|
||||
<!-- <UButton
|
||||
class="w-full my-1">
|
||||
Kalender
|
||||
</UButton>
|
||||
</UButton>-->
|
||||
<UButton
|
||||
class="w-full my-1"
|
||||
to="/standardEntity/customers"
|
||||
icon="i-heroicons-user-group"
|
||||
>
|
||||
Kunden
|
||||
</UButton>
|
||||
<UButton
|
||||
class="w-full my-1"
|
||||
to="/standardEntity/vendors"
|
||||
icon="i-heroicons-truck"
|
||||
>
|
||||
Lieferanten
|
||||
</UButton>
|
||||
<UButton
|
||||
class="w-full my-1"
|
||||
to="/standardEntity/contacts"
|
||||
icon="i-heroicons-user-group"
|
||||
>
|
||||
Ansprechpartner
|
||||
</UButton>
|
||||
<UButton
|
||||
class="w-full my-1"
|
||||
to="/standardEntity/plants"
|
||||
icon="i-heroicons-clipboard-document"
|
||||
>
|
||||
Objekte
|
||||
</UButton>
|
||||
|
||||
@@ -59,6 +59,18 @@
|
||||
<td>zahlungsziel_in_tagen</td>
|
||||
<td>Zahlungsziel in Tagen</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>lohnkosten</td>
|
||||
<td>Lohnkosten Verkauf</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>titel</td>
|
||||
<td>Titel</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>anrede</td>
|
||||
<td>Anrede</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</UCard>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import dayjs from "dayjs";
|
||||
import VueDatePicker from '@vuepic/vue-datepicker'
|
||||
import '@vuepic/vue-datepicker/dist/main.css'
|
||||
import {setPageLayout} from "#app";
|
||||
|
||||
|
||||
definePageMeta({
|
||||
@@ -32,8 +33,18 @@ const runningTimeInfo = ref({})
|
||||
const showConfigTimeModal = ref(false)
|
||||
const configTimeMode = ref("create")
|
||||
|
||||
const platform = ref("default")
|
||||
|
||||
const projects = ref([])
|
||||
|
||||
const setup = async () => {
|
||||
times.value = await useSupabaseSelect("times","*, profile(*), project(id, name)")
|
||||
projects.value = await useSupabaseSelect("projects","*")
|
||||
|
||||
if(await useCapacitor().getIsPhone()) {
|
||||
platform.value = "mobile"
|
||||
setPageLayout("mobile")
|
||||
}
|
||||
|
||||
runningTimeInfo.value = (await supabase
|
||||
.from("times")
|
||||
@@ -69,8 +80,6 @@ const filteredRows = computed(() => {
|
||||
|
||||
const itemInfo = ref({
|
||||
user: "",
|
||||
start: new Date(),
|
||||
end: "",
|
||||
notes: null,
|
||||
project: null,
|
||||
type: null,
|
||||
@@ -166,15 +175,31 @@ const createTime = async () => {
|
||||
itemInfo.value = {}
|
||||
toast.add({title: "Zeit erfolgreich erstellt"})
|
||||
showConfigTimeModal.value = false
|
||||
await dataStore.fetchTimes()
|
||||
|
||||
setup()
|
||||
}
|
||||
}
|
||||
|
||||
const openTime = (row) => {
|
||||
itemInfo.value = row
|
||||
|
||||
itemInfo.value.project = itemInfo.value.project.id
|
||||
itemInfo.value.profile = itemInfo.value.profile.id
|
||||
|
||||
showConfigTimeModal.value = true
|
||||
configTimeMode.value = "edit"
|
||||
|
||||
|
||||
}
|
||||
|
||||
const updateTime = async () => {
|
||||
let data = itemInfo.value
|
||||
|
||||
data.profile = data.profile.id
|
||||
data.project = data.project.id
|
||||
|
||||
const {error} = await supabase
|
||||
.from("times")
|
||||
.update(itemInfo.value)
|
||||
.update(data)
|
||||
.eq('id',itemInfo.value.id)
|
||||
|
||||
if(error) {
|
||||
@@ -183,7 +208,7 @@ const updateTime = async () => {
|
||||
|
||||
toast.add({title: "Zeit erfolgreich gespeichert"})
|
||||
showConfigTimeModal.value = false
|
||||
await dataStore.fetchTimes()
|
||||
setup()
|
||||
}
|
||||
|
||||
const format = (date) => {
|
||||
@@ -192,7 +217,7 @@ const format = (date) => {
|
||||
return `${dateFormat}`;
|
||||
}
|
||||
|
||||
const getDuration = (time) => {
|
||||
/*const getDuration = (time) => {
|
||||
const dez = dayjs(time.end).diff(time.start,'hour',true).toFixed(2)
|
||||
const hours = Math.floor(dez)
|
||||
const minutes = Math.floor((dez - hours) * 60)
|
||||
@@ -202,18 +227,30 @@ const getDuration = (time) => {
|
||||
minutes: minutes,
|
||||
composed: `${hours}:${minutes}`
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
const setState = async (newState) => {
|
||||
itemInfo.value.state = newState
|
||||
await updateTime()
|
||||
}
|
||||
|
||||
const getSecondInfo = (item) => {
|
||||
let returnArray = []
|
||||
|
||||
if(item.type) returnArray.push(item.type)
|
||||
if(item.project) returnArray.push(item.project.name)
|
||||
if(item.notes) returnArray.push(item.notes)
|
||||
|
||||
return returnArray
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UDashboardNavbar title="Zeiterfassung">
|
||||
|
||||
<UDashboardNavbar title="Projektzeiten">
|
||||
<template #toggle>
|
||||
<div v-if="platform === 'mobile'"></div>
|
||||
</template>
|
||||
</UDashboardNavbar>
|
||||
<UDashboardToolbar>
|
||||
<template #left>
|
||||
@@ -230,11 +267,13 @@ const setState = async (newState) => {
|
||||
Stop
|
||||
</UButton>
|
||||
<UButton
|
||||
@click="configTimeMode = 'create'; itemInfo = {start: new Date(), end: new Date(), user: user.id}; showConfigTimeModal = true"
|
||||
v-if="platform !== 'mobile'"
|
||||
@click="configTimeMode = 'create'; itemInfo = {startDate: new Date(), endDate: new Date()}; showConfigTimeModal = true"
|
||||
>
|
||||
Erstellen
|
||||
</UButton>
|
||||
<USelectMenu
|
||||
v-if="platform !== 'mobile'"
|
||||
:options="profileStore.profiles"
|
||||
option-attribute="fullName"
|
||||
value-attribute="id"
|
||||
@@ -295,7 +334,7 @@ const setState = async (newState) => {
|
||||
>
|
||||
<UCard>
|
||||
<template #header>
|
||||
Zeiteintrag {{configTimeMode === 'create' ? "erstellen" : "bearbeiten"}}
|
||||
Projektzeit {{configTimeMode === 'create' ? "erstellen" : "bearbeiten"}}
|
||||
</template>
|
||||
<UFormGroup
|
||||
label="Start:"
|
||||
@@ -303,7 +342,7 @@ const setState = async (newState) => {
|
||||
<UPopover :popper="{ placement: 'bottom-start' }">
|
||||
<UButton
|
||||
icon="i-heroicons-calendar-days-20-solid"
|
||||
:label="itemInfo.startDate ? dayjs(itemInfo.startDate).format('DD.MM.YYYY') : 'Datum auswählen'"
|
||||
:label="itemInfo.startDate ? dayjs(itemInfo.startDate).format('DD.MM.YYYY HH:mm') : 'Datum auswählen'"
|
||||
variant="outline"
|
||||
/>
|
||||
|
||||
@@ -318,7 +357,7 @@ const setState = async (newState) => {
|
||||
<UPopover :popper="{ placement: 'bottom-start' }">
|
||||
<UButton
|
||||
icon="i-heroicons-calendar-days-20-solid"
|
||||
:label="itemInfo.endDate ? dayjs(itemInfo.endDate).format('DD.MM.YYYY') : 'Datum auswählen'"
|
||||
:label="itemInfo.endDate ? dayjs(itemInfo.endDate).format('DD.MM.YYYY HH:mm') : 'Datum auswählen'"
|
||||
variant="outline"
|
||||
/>
|
||||
|
||||
@@ -332,13 +371,12 @@ const setState = async (newState) => {
|
||||
>
|
||||
<USelectMenu
|
||||
:options="profileStore.profiles"
|
||||
v-model="itemInfo.user"
|
||||
v-model="itemInfo.profile"
|
||||
option-attribute="fullName"
|
||||
value-attribute="id"
|
||||
:disabled="(configTimeMode === 'create' ? false : itemInfo.state !== 'Entwurf') || (!dataStore.hasRight('createTime') || !useRole().hasRight('createOwnTime'))"
|
||||
>
|
||||
<template #label>
|
||||
{{profileStore.profiles.find(profile => profile.id === itemInfo.user) ? profileStore.profiles.find(profile => profile.id === itemInfo.user).fullName : "Benutzer auswählen"}}
|
||||
{{profileStore.profiles.find(profile => profile.id === itemInfo.profile) ? profileStore.profiles.find(profile => profile.id === itemInfo.profile).fullName : "Benutzer auswählen"}}
|
||||
</template>
|
||||
</USelectMenu>
|
||||
</UFormGroup>
|
||||
@@ -346,14 +384,13 @@ const setState = async (newState) => {
|
||||
label="Projekt:"
|
||||
>
|
||||
<USelectMenu
|
||||
:options="dataStore.projects"
|
||||
:options="projects"
|
||||
v-model="itemInfo.project"
|
||||
option-attribute="name"
|
||||
value-attribute="id"
|
||||
searchable
|
||||
searchable-placeholder="Suche..."
|
||||
:search-attributes="['name']"
|
||||
:disabled="configTimeMode === 'create' ? false : itemInfo.state !== 'Entwurf'"
|
||||
>
|
||||
<template #label>
|
||||
{{dataStore.projects.find(project => project.id === itemInfo.project) ? dataStore.projects.find(project => project.id === itemInfo.project).name : "Projekt auswählen"}}
|
||||
@@ -368,7 +405,6 @@ const setState = async (newState) => {
|
||||
:options="timeTypes"
|
||||
option-attribute="label"
|
||||
value-attribute="label"
|
||||
:disabled="configTimeMode === 'create' ? false : itemInfo.state !== 'Entwurf'"
|
||||
>
|
||||
<template #label>
|
||||
{{itemInfo.type ? itemInfo.type : "Kategorie auswählen"}}
|
||||
@@ -380,12 +416,11 @@ const setState = async (newState) => {
|
||||
>
|
||||
<UTextarea
|
||||
v-model="itemInfo.notes"
|
||||
:disabled="configTimeMode === 'create' ? false : itemInfo.state !== 'Entwurf'"
|
||||
/>
|
||||
</UFormGroup>
|
||||
|
||||
|
||||
<template #footer v-if="configTimeMode === 'create' || itemInfo.state === 'Entwurf'">
|
||||
<template #footer>
|
||||
<InputGroup>
|
||||
<UButton
|
||||
@click="createTime"
|
||||
@@ -396,7 +431,6 @@ const setState = async (newState) => {
|
||||
<UButton
|
||||
@click="updateTime"
|
||||
v-else-if="configTimeMode === 'edit'"
|
||||
v-if="itemInfo.state === 'Entwurf'"
|
||||
>
|
||||
Speichern
|
||||
</UButton>
|
||||
@@ -412,14 +446,29 @@ const setState = async (newState) => {
|
||||
</UCard>
|
||||
</UModal>
|
||||
|
||||
<UDashboardPanelContent class="w-full" v-if="platform === 'mobile'">
|
||||
<a
|
||||
v-for="item in filteredRows"
|
||||
class="my-1"
|
||||
>
|
||||
<p class="truncate text-left text-primary text-xl">{{dayjs(item.startDate).format("DD.MM.YYYY HH:mm")}} - {{dayjs(item.endDate).format("HH:mm")}}</p>
|
||||
<p class="text-sm">
|
||||
<span v-for="(i,index) in getSecondInfo(item)">
|
||||
{{i}}{{index < getSecondInfo(item).length - 1 ? " - " : ""}}
|
||||
</span>
|
||||
|
||||
</p>
|
||||
</a>
|
||||
</UDashboardPanelContent>
|
||||
|
||||
|
||||
<UTable
|
||||
v-else
|
||||
class="mt-3"
|
||||
:columns="columns"
|
||||
:rows="filteredRows"
|
||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
|
||||
@select="(row) => {configTimeMode = 'edit';
|
||||
itemInfo = row;
|
||||
showConfigTimeModal = true}"
|
||||
@select="(row) => openTime(row)"
|
||||
>
|
||||
<template #state-data="{row}">
|
||||
<span
|
||||
|
||||
@@ -169,7 +169,7 @@ export const useDataStore = defineStore('data', () => {
|
||||
numberRangeHolder: "customerNumber",
|
||||
historyItemHolder: "customer",
|
||||
supabaseSortColumn: "customerNumber",
|
||||
supabaseSelectWithInformation: "*, projects(*), plants(*), contracts(*), contacts(*), createddocuments(*), files(*), events(*)",
|
||||
supabaseSelectWithInformation: "*, projects(*), plants(*), contracts(*), contacts(*), createddocuments(*, statementallocations(*)), files(*), events(*)",
|
||||
filters: [{
|
||||
name: "Archivierte ausblenden",
|
||||
default: true,
|
||||
@@ -471,10 +471,16 @@ export const useDataStore = defineStore('data', () => {
|
||||
key: "salutation",
|
||||
label: "Anrede",
|
||||
inputType: "text",
|
||||
inputChangeFunction: function (row) {
|
||||
row.fullName = `${row.firstName} ${row.lastName}`
|
||||
}
|
||||
},{
|
||||
key: "title",
|
||||
label: "Titel",
|
||||
inputType: "text",
|
||||
inputChangeFunction: function (row) {
|
||||
row.fullName = `${row.firstName} ${row.lastName}`
|
||||
}
|
||||
},{
|
||||
key: "firstName",
|
||||
label: "Vorname",
|
||||
@@ -505,6 +511,8 @@ export const useDataStore = defineStore('data', () => {
|
||||
selectDataType: "customers",
|
||||
selectOptionAttribute: "name",
|
||||
selectSearchAttributes: ['name'],
|
||||
secondInfo: true,
|
||||
secondInfoKey: "name"
|
||||
},
|
||||
{
|
||||
key: "vendor",
|
||||
@@ -514,6 +522,8 @@ export const useDataStore = defineStore('data', () => {
|
||||
selectDataType: "vendors",
|
||||
selectOptionAttribute: "name",
|
||||
selectSearchAttributes: ['name'],
|
||||
secondInfo: true,
|
||||
secondInfoKey: "name"
|
||||
},
|
||||
{
|
||||
key: "role",
|
||||
@@ -906,9 +916,9 @@ export const useDataStore = defineStore('data', () => {
|
||||
inputTrailing: "EUR",
|
||||
inputChangeFunction: function (row) {
|
||||
if(row.markupPercentage) {
|
||||
row.sellingPrice = row.purchasePrice * (1+row.markupPercentage/100);
|
||||
row.sellingPrice = (row.purchasePrice * (1+row.markupPercentage/100)).toFixed(4)
|
||||
} else {
|
||||
row.sellingPrice = row.purchasePrice;
|
||||
row.sellingPrice = row.purchasePrice.toFixed(4)
|
||||
}
|
||||
}
|
||||
},{
|
||||
@@ -917,8 +927,12 @@ export const useDataStore = defineStore('data', () => {
|
||||
inputType: "number",
|
||||
inputTrailing: "%",
|
||||
inputChangeFunction: function (row) {
|
||||
if(row.sellingPrice) {
|
||||
row.sellingPrice = row.purchasePrice * (1+row.markupPercentage/100);
|
||||
if(row.purchasePrice && ! row.sellingPrice) {
|
||||
row.sellingPrice = (row.purchasePrice * (1+row.markupPercentage/100)).toFixed(4)
|
||||
} else if(row.sellingPrice && !row.purchasePrice) {
|
||||
row.purchasePrice = (row.sellingPrice / (1+row.markupPercentage/100)).toFixed(4)
|
||||
} else {
|
||||
row.sellingPrice = (row.purchasePrice * (1+row.markupPercentage/100)).toFixed(4)
|
||||
}
|
||||
}
|
||||
},{
|
||||
@@ -929,8 +943,10 @@ export const useDataStore = defineStore('data', () => {
|
||||
inputType: "number",
|
||||
inputTrailing: "EUR",
|
||||
inputChangeFunction: function (row) {
|
||||
if(row.markupPercentage) {
|
||||
row.purchasePrice = (row.sellingPrice / (1+row.markupPercentage/100)).toFixed(4);
|
||||
if(row.purchasePrice ) {
|
||||
row.markupPercentage = ((row.sellingPrice / row.purchasePrice - 1) * 100 ).toFixed(2)
|
||||
} else{
|
||||
row.purchasePrice = (row.sellingPrice / (1+row.markupPercentage/100)).toFixed(4)
|
||||
}
|
||||
}
|
||||
},{
|
||||
@@ -1045,6 +1061,8 @@ export const useDataStore = defineStore('data', () => {
|
||||
selectDataType: "customers",
|
||||
selectOptionAttribute: "name",
|
||||
selectSearchAttributes: ['name'],
|
||||
secondInfo: true,
|
||||
secondInfoKey: "name",
|
||||
},{
|
||||
key: "plant",
|
||||
label: "Objekt",
|
||||
@@ -1485,7 +1503,8 @@ export const useDataStore = defineStore('data', () => {
|
||||
createddocuments: {
|
||||
isArchivable: true,
|
||||
label: "Dokumente",
|
||||
labelSingle: "Dokument"
|
||||
labelSingle: "Dokument",
|
||||
supabaseSelectWithInformation: "*, files(*), statementallocations(*)",
|
||||
},
|
||||
incominginvoices: {
|
||||
label: "Eingangsrechnungen",
|
||||
|
||||
Reference in New Issue
Block a user