Changed Plants to Objects

Changes in outgoinginvoices
This commit is contained in:
2024-01-27 11:54:14 +01:00
parent 6f6e835b0a
commit 3167b6a20a
21 changed files with 1821 additions and 23 deletions

View File

@@ -44,7 +44,8 @@ const itemInfo = ref({
end: "",
notes: null,
projectId: null,
type: null
type: null,
state: "Entwurf"
})
@@ -300,9 +301,10 @@ const setState = async (newState) => {
:dark="useColorMode().value !== 'light'"
:format="format"
:preview-format="format"
:disabled="itemInfo.state !== 'Entwurf'"
:disabled="configTimeMode === 'create' ? false : itemInfo.state !== 'Entwurf'"
/>
</UFormGroup>
{{itemInfo.state === 'Entwurf'}}{{itemInfo.state}}
<UFormGroup
label="Ende:"
>
@@ -316,7 +318,7 @@ const setState = async (newState) => {
:dark="useColorMode().value !== 'light'"
:format="format"
:preview-format="format"
:disabled="itemInfo.state !== 'Entwurf'"
:disabled="configTimeMode === 'create' ? false : itemInfo.state !== 'Entwurf'"
/>
</UFormGroup>
<!-- <UFormGroup
@@ -334,7 +336,7 @@ const setState = async (newState) => {
v-model="itemInfo.user"
option-attribute="fullName"
value-attribute="id"
:disabled="itemInfo.state !== 'Entwurf'"
:disabled="configTimeMode === 'create' ? false : itemInfo.state !== 'Entwurf'"
>
<template #label>
{{dataStore.profiles.find(profile => profile.id === itemInfo.user) ? dataStore.profiles.find(profile => profile.id === itemInfo.user).firstName : "Benutzer auswählen"}}
@@ -352,7 +354,7 @@ const setState = async (newState) => {
searchable
searchable-placeholder="Suche..."
:search-attributes="['name']"
:disabled="itemInfo.state !== 'Entwurf'"
:disabled="configTimeMode === 'create' ? false : itemInfo.state !== 'Entwurf'"
>
<template #label>
{{dataStore.projects.find(project => project.id === itemInfo.projectId) ? dataStore.projects.find(project => project.id === itemInfo.projectId).name : "Projekt auswählen"}}
@@ -367,7 +369,7 @@ const setState = async (newState) => {
:options="timeTypes"
option-attribute="label"
value-attribute="label"
:disabled="itemInfo.state !== 'Entwurf'"
:disabled="configTimeMode === 'create' ? false : itemInfo.state !== 'Entwurf'"
>
<template #label>
{{itemInfo.type ? itemInfo.type : "Kategorie auswählen"}}
@@ -379,7 +381,7 @@ const setState = async (newState) => {
>
<UTextarea
v-model="itemInfo.notes"
:disabled="itemInfo.state !== 'Entwurf'"
:disabled="configTimeMode === 'create' ? false : itemInfo.state !== 'Entwurf'"
/>
</UFormGroup>

View File

@@ -0,0 +1,835 @@
<script setup>
import dayjs from "dayjs"
const dataStore = useDataStore()
const user = useSupabaseUser()
const tabItems = [
{
label: "Editor"
},
{
label: "Vorschau"
}
]
const itemInfo = ref({
customer: 13,
contact: 8,
address: {
street: null,
special: null,
zip: null,
city: null,
},
project: 6,
documentNumber: "RE23-1409",
documentDate: "10.01.2024",
deliveryDate: "10.01.2024",
dateOfPerformance: null,
createdBy: user.value.id,
title: "TITEL",
description: "BESCHREIBUNG",
startText: "Sehr geehrte Frau Sindern,\n" +
"wir bedanken uns für Ihr entgegengebrachtes Vertrauen und Ihren Auftrag und stellen Ihnen\n" +
"folgende Positionen in Rechnung: ",
endText: "Bitte überweisen Sie den Rechnungsbetrag unter Angabe der Rechnungsnummer im Verwendungszweck innerhalb von 10 Tagen auf das unten angegebene Konto. Wir bedanken uns für das entgegengebrachte Vertrauen und freuen uns auf eine weitere gute Zusammenarbeit.",
rows: [
{
id: 1,
pos: 1,
mode: "free",
text: "FahrtkostenFahrtkostenFahrtkostenFahrtkosten",
quantity: 204,
unit: 3,
price: 0.80,
taxPercent: 19,
discountPercent: 0
},
{
id: 2,
pos: 2,
mode: "free",
text: "Test",
quantity: 1,
unit: 1,
price: 10,
taxPercent: 19,
discountPercent: 0
}
]
})
const getRowAmount = (row) => {
return String(Number(Number(row.quantity) * Number(row.price) * (1 - Number(row.discountPercent) /100) ).toFixed(2)).replace('.',',')
}
const addPosition = (mode) => {
let lastId = 0
itemInfo.value.rows.forEach(row => {
if(row.id > lastId) lastId = row.id
})
if(mode === 'free'){
itemInfo.value.rows.push({
id: lastId +1,
mode: "free",
text: "",
quantity: 1,
unit: 1,
price: 0,
taxPercent: 19,
discountPercent: 0
})
} else if(mode === 'normal'){
itemInfo.value.rows.push({
id: lastId +1,
mode: "normal",
quantity: 1,
price: 0,
taxPercent: 19,
discountPercent: 0
})
} else if(mode === "pagebreak") {
itemInfo.value.rows.push({
id: lastId +1,
mode: "pagebreak",
})
}
setPosNumbers()
}
const removePosition = (id) => {
let rows = itemInfo.value.rows.filter(row => row.id !== id)
/*rows = rows.sort((a,b) => a.pos - b.pos)
rows.forEach((row,index) => {
rows[index] = {...row, pos: index + 1}
})*/
itemInfo.value.rows = rows
setPosNumbers()
}
const documentTotal = computed(() => {
let totalNet = 0
let total19 = 0
let totalGross = 0
itemInfo.value.rows.forEach(row => {
if(row.mode === 'free' || row.mode === 'normal'){
let rowPrice = Number(Number(row.quantity) * Number(row.price)).toFixed(2)
totalNet += Number(rowPrice)
if(row.taxPercent === 19) {
total19 += Number(rowPrice * 0.19)
}
}
})
return {
totalNet: `${String(totalNet.toFixed(2)).replace(".",",")}`,
total19: `${String(total19.toFixed(2)).replace(".",",")}`,
totalGross: `${String(Number(totalNet + total19).toFixed(2)).replace(".",",")}`
}
})
const getDocumentData = () => {
let customerData = dataStore.getCustomerById(itemInfo.value.customer)
let contactData = dataStore.getContactById(itemInfo.value.contact)
let userData = dataStore.getProfileById(user.value.id)
let rows = itemInfo.value.rows.map(row => {
let unit = dataStore.units.find(i => i.id === row.unit)
if(row.mode === 'free' || row.mode === 'normal') {
if(row.mode === 'normal') row.text = dataStore.getProductById(row.product).name
return {
...row,
rowAmount: `${getRowAmount(row)}`,
quantity: String(row.quantity).replace(".",","),
unit: unit.short,
pos: String(row.pos),
price: `${String(row.price.toFixed(2)).replace(".",",")}`,
discountText: `(Rabatt: ${row.discountPercent} %)`
}
} else {
return row
}
})
const returnData = {
recipient: {
name: customerData.name,
contact: `${contactData.firstName} ${contactData.lastName}`,
street: customerData.infoData.street,
special: "",
city: customerData.infoData.city,
zip: customerData.infoData.zip
},
info: {
customerNumber: customerData.customerNumber,
documentNumber: itemInfo.value.documentNumber,
documentDate: dayjs(itemInfo.value.documentDate, 'DD.MM.YYYY').format("DD.MM.YYYY"),
deliveryDate: dayjs(itemInfo.value.deliveryDate, 'DD.MM.YYYY').format("DD.MM.YYYY"),
contactPerson: userData.fullName ||"",
contactTel: userData.mobileTel ||"",
contactEMail: userData.email,
project: dataStore.getProjectById(itemInfo.value.project).name
},
title: itemInfo.value.title,
description: itemInfo.value.description,
endText: itemInfo.value.endText,
startText: itemInfo.value.startText,
rows: rows,
total: documentTotal.value
}
console.log(returnData)
return returnData
}
const showDocument = ref(false)
const uri = ref("")
const generateDocument = async () => {
uri.value = await useCreatePdf(getDocumentData())
//alert(uri.value)
showDocument.value = true
}
const onChangeTab = (index) => {
if(index === 1) {
generateDocument()
}
}
const setPosNumbers = () => {
let index = 1
let rows = itemInfo.value.rows.map(row => {
if(row.mode === 'free' ||row.mode === 'normal') {
row.pos = index
index += 1
}
return row
})
}
</script>
<template>
<!-- <UButton
@click="createPdf"
>TEST</UButton>-->
<UCard class="h-fit">
<UTabs :items="tabItems" @change="onChangeTab">
<template #item="{item}">
<div v-if="item.label === 'Editor'">
<InputGroup>
<div class="flex-auto mr-5">
<UFormGroup
label="Kunde:"
>
<USelectMenu
:options="dataStore.customers"
option-attribute="name"
value-attribute="id"
:search-attributes="['name']"
searchable
searchable-placeholder="Suche..."
v-model="itemInfo.customer"
>
<template #label>
{{dataStore.getCustomerById(itemInfo.customer) ? dataStore.getCustomerById(itemInfo.customer).name : "Kein Kunde ausgewählt"}}
</template>
</USelectMenu>
</UFormGroup>
<UFormGroup
label="Ansprechpartner:"
>
<USelectMenu
:options="dataStore.getContactsByCustomerId(itemInfo.customer)"
option-attribute="fullName"
value-attribute="id"
:search-attributes="['name']"
searchable
searchable-placeholder="Suche..."
v-model="itemInfo.contact"
>
<template #label>
{{dataStore.getContactById(itemInfo.contact) ? dataStore.getContactById(itemInfo.contact).fullName : "Kein Kontakt ausgewählt"}}
</template>
</USelectMenu>
</UFormGroup>
<UFormGroup
label="Adresse:"
>
<UInput
v-model="itemInfo.address.street"
:placeholder="dataStore.getCustomerById(itemInfo.customer) ? dataStore.getCustomerById(itemInfo.customer).infoData.street : 'Straße + Hausnummer'"
/>
<UInput
v-model="itemInfo.address.special"
class="mt-3"
:placeholder="dataStore.getCustomerById(itemInfo.customer) ? dataStore.getCustomerById(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'"
/>
<UInput
class="flex-auto"
v-model="itemInfo.address.city"
:placeholder="dataStore.getCustomerById(itemInfo.customer) ? dataStore.getCustomerById(itemInfo.customer).infoData.city : 'Ort'"
/>
</InputGroup>
</UFormGroup>
</div>
<div class="flex-auto">
<UFormGroup
label="Rechnungsnummer:"
>
<UInput
v-model="itemInfo.documentNumber"
/>
</UFormGroup>
<UFormGroup
label="Datum:"
>
<UInput
v-model="itemInfo.documentDate"
/>
</UFormGroup>
<UFormGroup
label="Lieferdatum:"
>
<UInput
v-model="itemInfo.deliveryDate"
/>
</UFormGroup>
<UFormGroup
label="Ansprechpartner:"
>
<UInput
/>
</UFormGroup>
<UFormGroup
label="Kontakt Telefon:"
>
<UInput
/>
</UFormGroup>
<UFormGroup
label="Kontakt E-Mail:"
>
<UInput
/>
</UFormGroup><UFormGroup
label="Kontakt E-Mail:"
>
<UInput
/>
</UFormGroup>
<UFormGroup
label="Projekt:"
>
<USelectMenu
:options="dataStore.projects"
v-model="itemInfo.project"
value-attribute="id"
option-attribute="name"
searchable
searchable-placeholder="Suche..."
:search-attributes="['name']"
>
<template #label>
{{dataStore.getProjectById(itemInfo.project) ? dataStore.getProjectById(itemInfo.project).name : "Kein Projekt ausgewählt"}}
</template>
<template #option="{option: project}">
{{dataStore.getCustomerById(project.customer).name}} - {{project.name}}
</template>
</USelectMenu>
</UFormGroup>
</div>
</InputGroup>
<UDivider
class="my-3"
/>
<UFormGroup
label="Titel:"
>
<UInput v-model="itemInfo.title"/>
</UFormGroup>
<UFormGroup
label="Beschreibung:"
class="mt-3"
>
<UInput v-model="itemInfo.description"/>
</UFormGroup>
<UDivider
class="my-3"
/>
<UFormGroup
label="Einleitung:"
>
<UTextarea
v-model="itemInfo.startText"
/>
</UFormGroup>
<UDivider
class="my-3"
/>
<table class="w-full">
<thead>
<tr>
<th></th>
<th>Pos.</th>
<th>Produkt / Leistung</th>
<th>Menge</th>
<th>Einheit</th>
<th>Preis</th>
<th>Steuer</th>
<th>Rabatt</th>
<th>Gesamt</th>
</tr>
</thead>
<draggable
v-model="itemInfo.rows"
handle=".handle"
tag="tbody"
itemKey="pos"
@end="setPosNumbers"
>
<template #item="{element: row}">
<tr
>
<td>
<UIcon
class="handle"
name="i-mdi-menu"
/>
</td>
<td
v-if="row.mode === 'pagebreak'"
colspan="8"
>
<UDivider/>
</td>
<td
v-if="row.mode === 'free' || row.mode === 'normal'"
>{{row.pos}}</td>
<td
class="w-120"
v-if="row.mode === 'free'"
>
<UInput
v-model="row.text"
maxlength="40"
placeholder="Name"
/>
</td>
<td
class="w-120"
v-else-if="row.mode === 'normal'"
>
<USelectMenu
:options="dataStore.products"
option-attribute="name"
value-attribute="id"
searchable
searchable-placeholder="Suche ..."
:search-attributes="['name']"
v-model="row.product"
@change="row.unit = dataStore.getProductById(row.product).unit,
row.price = dataStore.getProductById(row.product).sellingPrice || 0"
>
<template #label>
{{dataStore.getProductById(row.product) ?dataStore.getProductById(row.product).name : "Kein Produkt ausgewählt" }}
</template>
</USelectMenu>
</td>
<td
class="w-20"
v-if="row.mode === 'free' || row.mode === 'normal'"
>
<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' "
/>
</td>
<td
class="w-40"
v-if="row.mode === 'free' || row.mode === 'normal'"
>
<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>
</td>
<td
v-if="row.mode === 'free' || row.mode === 'normal'"
>
<UInput
v-model="row.price"
type="number"
step="0.01"
>
<template #trailing>
<span class="text-gray-500 dark:text-gray-400 text-xs">EUR</span>
</template>
</UInput>
</td>
<td
class="w-40"
v-if="row.mode === 'free' || row.mode === 'normal'"
>
<USelectMenu
:options="['19','7','0']"
v-model="row.taxPercent"
>
<template #option="{option}">
{{option}} %
</template>
<template #label>
{{row.taxPercent}} %
</template>
</USelectMenu>
</td>
<td
class="w-40"
v-if="row.mode === 'free' || row.mode === 'normal'"
>
<UInput
v-model="row.discountPercent"
type="number"
step="0.01"
placeholder="0"
>
<template #trailing>
<span class="text-gray-500 dark:text-gray-400 text-xs">%</span>
</template>
</UInput>
</td>
<td
v-if="row.mode === 'free' || row.mode === 'normal'"
>
<div class="text-right font-bold">{{getRowAmount(row)}} </div>
</td>
<td>
<UButton
variant="ghost"
color="rose"
icon="i-heroicons-x-mark-16-solid"
@click="removePosition(row.id)"
/>
</td>
</tr>
</template>
</draggable>
<!-- <template v-for="row in itemInfo.rows">
<tr
>
<td
v-if="row.mode === 'pagebreak'"
colspan="8"
>
<UDivider/>
</td>
<td
rowspan="2"
v-if="row.mode === 'free' || row.mode === 'normal'"
>{{row.pos}}</td>
<td
class="w-120"
v-if="row.mode === 'free'"
>
<UInput
v-model="row.text"
maxlength="40"
placeholder="Name"
/>
</td>
<td
class="w-120"
v-else-if="row.mode === 'normal'"
>
<USelectMenu
:options="dataStore.products"
option-attribute="name"
value-attribute="id"
searchable
searchable-placeholder="Suche ..."
:search-attributes="['name']"
v-model="row.product"
@change="row.unit = dataStore.getProductById(row.product).unit,
row.price = dataStore.getProductById(row.product).sellingPrice || 0"
>
<template #label>
{{dataStore.getProductById(row.product) ?dataStore.getProductById(row.product).name : "Kein Produkt ausgewählt" }}
</template>
</USelectMenu>
</td>
<td
class="w-20"
v-if="row.mode === 'free' || row.mode === 'normal'"
>
<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' "
/>
</td>
<td
class="w-40"
v-if="row.mode === 'free' || row.mode === 'normal'"
>
<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>
</td>
<td
v-if="row.mode === 'free' || row.mode === 'normal'"
>
<UInput
v-model="row.price"
type="number"
step="0.01"
>
<template #trailing>
<span class="text-gray-500 dark:text-gray-400 text-xs">EUR</span>
</template>
</UInput>
</td>
<td
class="w-40"
v-if="row.mode === 'free' || row.mode === 'normal'"
>
<USelectMenu
:options="['19','7','0']"
v-model="row.taxPercent"
>
<template #option="{option}">
{{option}} %
</template>
<template #label>
{{row.taxPercent}} %
</template>
</USelectMenu>
&lt;!&ndash; <UInput
v-model="row.taxPercent"
>
<template #trailing>
<span class="text-gray-500 dark:text-gray-400 text-xs">%</span>
</template>
</UInput>&ndash;&gt;
</td>
<td
class="w-40"
v-if="row.mode === 'free' || row.mode === 'normal'"
>
<UInput
v-model="row.discountPercent"
type="number"
step="0.01"
placeholder="0"
>
<template #trailing>
<span class="text-gray-500 dark:text-gray-400 text-xs">%</span>
</template>
</UInput>
</td>
<td
v-if="row.mode === 'free' || row.mode === 'normal'"
rowspan="2"
>
<div class="text-right font-bold">{{getRowAmount(row)}} </div>
</td>
<td rowspan="2">
<UButton
variant="ghost"
color="rose"
icon="i-heroicons-x-mark-16-solid"
@click="removePosition(row.pos)"
/>
</td>
</tr>
<tr>
<td colspan="6">
<UTextarea
v-model="row.description"
placeholder="Beschreibung"
/>
</td>
</tr>
</template>-->
</table>
<table>
<tr>
<td class="font-bold">Netto:</td>
<td>{{documentTotal.totalNet}}</td>
</tr>
<tr>
<td class="font-bold">zzgl. 19 % USt:</td>
<td>{{documentTotal.total19}}</td>
</tr>
<tr>
<td class="font-bold">Brutto:</td>
<td>{{documentTotal.totalGross}}</td>
</tr>
</table>
<InputGroup>
<UButton
@click="addPosition('normal')"
class="mt-3"
>
+ Artikelposition
</UButton>
<UButton
@click="addPosition('free')"
class="mt-3"
>
+ Freitextposition
</UButton>
<UButton
@click="addPosition('pagebreak')"
class="mt-3"
>
+ Seitenumbruch
</UButton>
</InputGroup>
<UDivider
class="my-3"
/>
<UFormGroup
label="Nachbemerkung:"
>
<UTextarea
v-model="itemInfo.endText"
/>
</UFormGroup>
</div>
<div v-else-if="item.label === 'Vorschau'">
<!-- <UButton
@click="generateDocument"
>
Show
</UButton>-->
<object
:data="uri"
v-if="showDocument"
type="application/pdf"
class="w-full previewDocument"
/>
</div>
</template>
</UTabs>
</UCard>
</template>
<style scoped>
th {
text-align: left;
}
td {
padding: .4em;
}
/*tr:hover {
border: 1px solid #69c350;
}*/
.previewDocument {
height: 80vh;
}
</style>

View File

@@ -12,6 +12,11 @@ const router = useRouter()
const toast = useToast()
const id = ref(route.params.id ? route.params.id : null )
const editor = useEditor({
content: "<p>I'm running Tiptap with Vue.js. 🎉</p>",
extensions: [TiptapStarterKit],
});
let currentItem = null
//Working
@@ -25,6 +30,8 @@ const tabItems = [
label: "Projekte"
},{
label: "Aufgaben"
},{
label: "Dokumentation"
}
]
@@ -93,6 +100,11 @@ setupPage()
</UTable>
</div>
<div v-if="item.label === 'Dokumentation'">
<Editor/>
</div>
</template>
</UTabs>

View File

@@ -1,7 +1,7 @@
<template>
<div id="main">
<InputGroup>
<UButton @click="router.push(`/plants/create/`)">+ Anlage</UButton>
<UButton @click="router.push(`/plants/create/`)">+ Objekt</UButton>
<UInput
v-model="searchString"

View File

@@ -479,7 +479,7 @@ setupPage()
</UFormGroup>
<UFormGroup
label="Anlage:"
label="Objekt:"
>
<USelectMenu
v-model="itemInfo.plant"
@@ -490,7 +490,7 @@ setupPage()
:search-attributes="['name']"
>
<template #label>
{{dataStore.getPlantById(itemInfo.plant) ? dataStore.getPlantById(itemInfo.plant).name : "Anlage auswählen"}}
{{dataStore.getPlantById(itemInfo.plant) ? dataStore.getPlantById(itemInfo.plant).name : "Objekt auswählen"}}
</template>
</USelectMenu>
</UFormGroup>

View File

@@ -54,7 +54,7 @@ const itemColumns = [
},
{
key: "plant",
label: "Anlage",
label: "Objekt",
sortable: true
}
]

View File

@@ -55,7 +55,7 @@ const itemColumns = [
const selectItem = (item) => {
console.log(item)
router.push(`/inventory/spaces/show/${item.id} `)
router.push(`/spaces/show/${item.id} `)
}

View File

@@ -162,7 +162,7 @@ setupPage()
</USelectMenu>
</UFormGroup>
<UFormGroup
label="Anlage:"
label="Objekt:"
>
<USelectMenu
v-model="itemInfo.plant"
@@ -174,7 +174,7 @@ setupPage()
:search-attributes="['name']"
>
<template #label>
{{dataStore.getPlantById(itemInfo.plant) ? dataStore.getPlantById(itemInfo.plant).name : "Keine Anlage ausgewählt"}}
{{dataStore.getPlantById(itemInfo.plant) ? dataStore.getPlantById(itemInfo.plant).name : "Kein Objekt ausgewählt"}}
</template>
</USelectMenu>
</UFormGroup>

View File

@@ -76,7 +76,7 @@ const columns = [
sortable: true
},{
key: "plant",
label: "Anlage:",
label: "Objekt:",
sortable: true
}
]