Files
FEDEO/frontend/pages/createDocument/edit/[[id]].vue
florianfederspiel 52c182cb5f
All checks were successful
Build and Push Docker Images / build-backend (push) Successful in 3m8s
Build and Push Docker Images / build-frontend (push) Successful in 1m15s
Fixes
2026-03-04 20:44:19 +01:00

3228 lines
125 KiB
Vue

<script setup>
import dayjs from "dayjs"
import Handlebars from "handlebars"
import { v4 as uuidv4 } from 'uuid';
import {useFunctions} from "~/composables/useFunctions.js";
import EntityModalButtons from "~/components/EntityModalButtons.vue";
const dataStore = useDataStore()
const profileStore = useProfileStore()
const route = useRoute()
const router = useRouter()
const modal = useModal()
const auth = useAuthStore()
const guard = ref(true)
const itemInfo = ref({
type: "invoices",
taxType: "Standard",
customer: null,
contact: null,
address: {
street: null,
special: null,
zip: null,
city: null,
},
project: null,
documentNumber: null,
documentNumberTitle: "Rechnungsnummer",
documentDate: dayjs(),
deliveryDate: dayjs(),
deliveryDateEnd: null,
deliveryDateType: "Lieferdatum",
dateOfPerformance: null,
paymentDays: auth.activeTenantData.standardPaymentDays,
payment_type: "transfer",
customSurchargePercentage: 0,
created_by: auth.user.id,
title: null,
description: null,
startText: null,
endText: null,
rows: [],
contactPerson: auth.user.id,
contactPersonName: null,
contactTel: null,
contactEMail: null,
serialConfig: {
firstExecution: "",
intervall: "monatlich",
executionUntil: "",
active: true,
dateDirection: "Rückwirkend",
},
letterhead: null,
agriculture: {},
usedAdvanceInvoices: []
})
console.log(auth.activeTenantData)
const letterheads = ref([])
const createddocuments = ref([])
const projects = ref([])
const plants = ref([])
const products = ref([])
const productcategories = ref([])
const selectedProductcategorie = ref(null)
const services = ref([])
const servicecategories = ref([])
const selectedServicecategorie = ref(null)
const customers = ref([])
const contacts = ref([])
const contracts = ref([])
const texttemplates = ref([])
const units = ref([])
const tenantUsers = ref([])
const setupData = async () => {
letterheads.value = (await useEntities("letterheads").select("*")).filter(i => i.documentTypes.length === 0 || i.documentTypes.includes(itemInfo.value.type))
createddocuments.value = await useEntities("createddocuments").select("*")
projects.value = await useEntities("projects").select("*")
plants.value = await useEntities("plants").select("*")
services.value = await useEntities("services").select("*")
servicecategories.value = await useEntities("servicecategories").select("*")
products.value = await useEntities("products").select("*")
productcategories.value = await useEntities("productcategories").select("*")
customers.value = await useEntities("customers").select("*", "customerNumber")
contacts.value = await useEntities("contacts").select("*")
contracts.value = await useEntities("contracts").select("*")
texttemplates.value = await useEntities("texttemplates").select("*")
units.value = await useEntities("units").selectSpecial("*")
tenantUsers.value = (await useNuxtApp().$api(`/api/tenant/users`, {
method: "GET"
})).users
}
const loaded = ref(false)
const normalizeEntityId = (value) => {
if (value === null || typeof value === "undefined") return null
return typeof value === "object" ? (value.id ?? null) : value
}
const setupPage = async () => {
await setupData()
if (productcategories.value.length > 0) selectedProductcategorie.value = productcategories.value[0].id
if (servicecategories.value.length > 0) selectedServicecategorie.value = servicecategories.value[0].id
if (route.params) {
if (route.params.id) {
console.log(route.params)
itemInfo.value = await useEntities("createddocuments").selectSingle(route.params.id,'',false)
await setContactPersonData()
checkCompatibilityWithInputPrice()
}
if (!itemInfo.value.deliveryDateType) itemInfo.value.deliveryDateType = "Lieferdatum"
if (itemInfo.value.rows.find(i => i.agriculture)) {
processDieselPosition()
}
}
if (route.query) {
if (route.query.type) itemInfo.value.type = route.query.type
if (!itemInfo.value.startText && !itemInfo.value.endText) {
setDocumentTypeConfig(true)
} else {
setDocumentTypeConfig(false)
}
if (route.query.linkedDocuments) {
console.log(route.query.loadMode)
if (route.query.loadMode === "deliveryNotes") {
let linkedDocuments = (await useEntities("createddocuments").select()).filter(i => JSON.parse(route.query.linkedDocuments).includes(i.id))
if (linkedDocuments.length === 0) return
//TODO: Implement Checking for Same Customer, Contact and Project
itemInfo.value.customer = normalizeEntityId(linkedDocuments[0].customer)
itemInfo.value.project = normalizeEntityId(linkedDocuments[0].project)
itemInfo.value.contact = normalizeEntityId(linkedDocuments[0].contact)
await setCustomerData(null, true)
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
])
})
itemInfo.value.deliveryDateType = "Leistungszeitraum"
itemInfo.value.deliveryDate = firstDate
itemInfo.value.deliveryDateEnd = lastDate
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: "Allgemein"
})
processDieselPosition()
}
}
else if (route.query.loadMode === "finalInvoice") {
let linkedDocuments = (await useEntities("createddocuments").select()).filter(i => JSON.parse(route.query.linkedDocuments).includes(i.id))
if (linkedDocuments.length === 0) return
//TODO: Implement Checking for Same Customer, Contact and Project
console.log(linkedDocuments)
itemInfo.value.customer = normalizeEntityId(linkedDocuments[0].customer)
itemInfo.value.project = normalizeEntityId(linkedDocuments[0].project)
itemInfo.value.contact = normalizeEntityId(linkedDocuments[0].contact)
await setCustomerData(null, true)
for await (const doc of linkedDocuments.filter(i => i.type === "confirmationOrders")) {
let linkedDocument = await useEntities("createddocuments").selectSingle(doc.id)
itemInfo.value.rows.push({
id: uuidv4(),
mode: "title",
text: linkedDocument.title,
})
itemInfo.value.rows.push(...linkedDocument.rows)
}
for await (const doc of linkedDocuments.filter(i => i.type === "quotes")) {
let linkedDocument = await useEntities("createddocuments").selectSingle(doc.id)
itemInfo.value.rows.push({
id: uuidv4(),
mode: "title",
text: linkedDocument.title,
})
itemInfo.value.rows.push(...linkedDocument.rows)
}
itemInfo.value.rows.push({
id: uuidv4(),
mode: "title",
text: "Abschlagsrechnungen",
})
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.createddocument) {
itemInfo.value.createddocument = route.query.createddocument
let linkedDocument = await useEntities("createddocuments").selectSingle(itemInfo.value.createddocument,'',false)
if (route.query.optionsToImport) {
//Import only true
let optionsToImport = JSON.parse(route.query.optionsToImport)
console.log(optionsToImport)
console.log(linkedDocument)
if (optionsToImport.taxType) itemInfo.value.taxType = linkedDocument.taxType
if (optionsToImport.customer) itemInfo.value.customer = linkedDocument.customer
if (optionsToImport.letterhead) itemInfo.value.letterhead = linkedDocument.letterhead
if (optionsToImport.contact) itemInfo.value.contact = linkedDocument.contact
if (optionsToImport.deliveryDateType) itemInfo.value.deliveryDateType = linkedDocument.deliveryDateType
if (optionsToImport.deliveryDate) itemInfo.value.deliveryDate = linkedDocument.deliveryDate
if (optionsToImport.deliveryDateEnd) itemInfo.value.deliveryDateEnd = linkedDocument.deliveryDateEnd
if (optionsToImport.documentDate) itemInfo.value.documentDate = linkedDocument.documentDate
if (optionsToImport.paymentDays) itemInfo.value.paymentDays = linkedDocument.paymentDays
if (optionsToImport.payment_type) itemInfo.value.payment_type = linkedDocument.payment_type
if (optionsToImport.customSurchargePercentage) itemInfo.value.customSurchargePercentage = linkedDocument.customSurchargePercentage
if (optionsToImport.contactPerson) itemInfo.value.contactPerson = linkedDocument.contactPerson
if (optionsToImport.plant) itemInfo.value.plant = linkedDocument.plant
if (optionsToImport.project) itemInfo.value.project = linkedDocument.project
if (optionsToImport.title) itemInfo.value.title = linkedDocument.title
if (optionsToImport.description) itemInfo.value.description = linkedDocument.description
if (optionsToImport.startText) itemInfo.value.startText = linkedDocument.startText
if (optionsToImport.rows) itemInfo.value.rows = linkedDocument.rows
if (optionsToImport.endText) itemInfo.value.endText = linkedDocument.endText
} 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
itemInfo.value.deliveryDate = linkedDocument.deliveryDate
itemInfo.value.deliveryDateEnd = linkedDocument.deliveryDateEnd
itemInfo.value.documentDate = linkedDocument.documentDate
itemInfo.value.paymentDays = linkedDocument.paymentDays
itemInfo.value.payment_type = linkedDocument.payment_type
itemInfo.value.customSurchargePercentage = linkedDocument.customSurchargePercentage
itemInfo.value.contactPerson = linkedDocument.contactPerson
itemInfo.value.plant = linkedDocument.plant
itemInfo.value.project = linkedDocument.project
itemInfo.value.title = linkedDocument.title
itemInfo.value.description = linkedDocument.description
itemInfo.value.startText = linkedDocument.startText
itemInfo.value.rows = linkedDocument.rows
itemInfo.value.endText = linkedDocument.endText
}
checkCompatibilityWithInputPrice()
if (route.query.loadMode === "storno") {
itemInfo.value.rows.forEach(row => {
row.price = row.price * -1
})
itemInfo.value.documentDate = dayjs()
itemInfo.value.usedAdvanceInvoices = linkedDocument.usedAdvanceInvoices
itemInfo.value.description = `Stornorechnung zu Rechnung ${linkedDocument.documentNumber} vom ${dayjs(linkedDocument.documentDate).format('DD.MM.YYYY')}`
itemInfo.value.type = "cancellationInvoices"
setDocumentTypeConfig(true)
}
await setCustomerData(null, true)
}
if (route.query.project) {
itemInfo.value.project = Number(route.query.project)
let project = await useEntities("projects").selectSingle(itemInfo.value.project)
if (!itemInfo.value.description) {
itemInfo.value.description = project.customerRef
}
}
if (route.query.plant) {
itemInfo.value.plant = Number(route.query.plant)
}
if (route.query.contact) itemInfo.value.contact = Number(route.query.contact)
if (route.query.customer) {
itemInfo.value.customer = Number(route.query.customer)
setCustomerData()
}
}
loaded.value = true
}
setupPage()
const setDocumentTypeConfig = (withTexts = false) => {
if (itemInfo.value.type === "invoices" || itemInfo.value.type === "advanceInvoices" || itemInfo.value.type === "serialInvoices" || itemInfo.value.type === "cancellationInvoices") {
itemInfo.value.documentNumberTitle = "Rechnungsnummer"
itemInfo.value.title = `Rechnung-Nr. ${itemInfo.value.documentNumber ? itemInfo.value.documentNumber : "XXXX"}`
} else if (itemInfo.value.type === "quotes") {
itemInfo.value.documentNumberTitle = "Angebotsnummer"
itemInfo.value.title = `Angebot-Nr. ${itemInfo.value.documentNumber ? itemInfo.value.documentNumber : "XXXX"}`
} else if (itemInfo.value.type === "deliveryNotes") {
itemInfo.value.documentNumberTitle = "Lieferscheinnummer"
itemInfo.value.title = `Lieferschein-Nr. ${itemInfo.value.documentNumber ? itemInfo.value.documentNumber : "XXXX"}`
} else if (itemInfo.value.type === "confirmationOrders") {
itemInfo.value.documentNumberTitle = "Auftragsbestätigungsnr."
itemInfo.value.title = `Auftragsbestätigung-Nr. ${itemInfo.value.documentNumber ? itemInfo.value.documentNumber : "XXXX"}`
}
if (withTexts) {
itemInfo.value.startText = getTextTemplateByType(itemInfo.value.type).find(i => i.default && i.pos === "startText").text
itemInfo.value.endText = getTextTemplateByType(itemInfo.value.type).find(i => i.default && i.pos === "endText").text
//itemInfo.value.startText = texttemplates.value.find(i => i.documentType === itemInfo.value.type && i.default && i.pos === "startText").text
//itemInfo.value.endText = texttemplates.value.find(i => i.documentType === itemInfo.value.type && i.default && i.pos === "endText").text
}
itemInfo.value.letterhead = letterheads.value[0].id
if (itemInfo.value.type === "advanceInvoices" && !itemInfo.value.rows.find(i => i.text === "Abschlagszahlung")) {
itemInfo.value.rows.push({
mode: "free",
text: "Abschlagszahlung",
quantity: 1,
unit: 10,
price: 0,
taxPercent: 19,
discountPercent: 0,
advanceInvoiceData: advanceInvoiceData.value
})
setPosNumbers()
}
}
const setTaxType = () => {
if (itemInfo.value.taxType === "19 UStG") {
itemInfo.value.rows.forEach(row => {
row.taxPercent = 0
})
} else if (itemInfo.value.taxType === "13b UStG") {
itemInfo.value.rows.forEach(row => {
row.taxPercent = 0
})
} else if (itemInfo.value.taxType === "12.3 UStG") {
itemInfo.value.rows.forEach(row => {
row.taxPercent = 0
})
}
}
const setCustomerData = async (customerId, loadOnlyAdress = false) => {
if (customerId) {
itemInfo.value.customer = customerId
}
customers.value = await useEntities("customers").select()
let customer = customers.value.find(i => i.id === itemInfo.value.customer)
console.log(customer)
if(!loadOnlyAdress) {
itemInfo.value.contact = null
}
if (customer) {
itemInfo.value.address.street = customer.infoData.street
itemInfo.value.address.zip = customer.infoData.zip
itemInfo.value.address.city = customer.infoData.city
itemInfo.value.address.special = customer.infoData.special
if (!loadOnlyAdress && customer.customPaymentDays) itemInfo.value.paymentDays = customer.customPaymentDays
if (!loadOnlyAdress && customer.custom_payment_type) itemInfo.value.payment_type = customer.custom_payment_type
if (!loadOnlyAdress) {
itemInfo.value.taxType = customer.customTaxType || "Standard"
setTaxType()
}
if (!loadOnlyAdress && customer.customSurchargePercentage) {
itemInfo.value.customSurchargePercentage = customer.customSurchargePercentage
updateCustomSurcharge()
}
if (!loadOnlyAdress && contacts.value.filter(i => i.customer === itemInfo.value.customer).length === 1) {
itemInfo.value.contact = contacts.value.filter(i => i.customer === itemInfo.value.customer)[0].id
}
}
}
const setContactPersonData = async () => {
//console.log(itemInfo.value.contactPerson) //TODO: BACKEND CHANGE Set Profile
if(itemInfo.value.created_by) {
let profile = (await useNuxtApp().$api(`/api/user/${itemInfo.value.created_by}`, {
method: "GET"
})).profile
itemInfo.value.contactPersonName = profile.full_name
itemInfo.value.contactTel = profile.mobile_tel || profile.fixed_tel || ""
itemInfo.value.contactEMail = profile.email
}
}
const showAdvanceInvoiceCalcModal = ref(false)
const advanceInvoiceData = ref({
totalSumNet: 0,
partPerPecentage: 0,
part: 0
})
const importPositions = () => {
if (itemInfo.value.type === 'advanceInvoices') {
if (advanceInvoiceData.value.totalSumNet !== 0 && advanceInvoiceData.value.partPerPecentage !== 0 && advanceInvoiceData.value.part !== 0) {
showAdvanceInvoiceCalcModal.value = false
let lastId = 0
itemInfo.value.rows.forEach(row => {
if (row.id > lastId) lastId = row.id
})
itemInfo.value.rows.push({
id: lastId + 1,
mode: "free",
text: "Abschlagszahlung",
quantity: 1,
unit: 10,
price: advanceInvoiceData.value.part,
taxPercent: 19,
discountPercent: 0,
advanceInvoiceData: advanceInvoiceData.value
})
setPosNumbers()
}
}
}
const getRowAmount = (row) => {
return String(Number(Number(row.quantity) * Number(row.price) * (1 - Number(row.discountPercent) / 100)).toFixed(2)).replace('.', ',')
}
const getRowAmountUndiscounted = (row) => {
return String(Number(Number(row.quantity) * Number(row.price)).toFixed(2)).replace('.', ',')
}
const addPosition = (mode) => {
let lastId = 0
itemInfo.value.rows.forEach(row => {
if (row.id > lastId) lastId = row.id
})
let taxPercentage = 19
if (['13b UStG', '19 UStG', '12.3 UStG'].includes(itemInfo.value.taxType)) {
taxPercentage = 0
}
if (mode === 'free') {
let rowData = {
id: uuidv4(),
mode: "free",
text: "",
quantity: 1,
unit: 1,
inputPrice: 0,
price: 0,
taxPercent: taxPercentage,
discountPercent: 0,
linkedEntitys: []
}
itemInfo.value.rows.push({...rowData, ...auth.activeTenantData.extraModules.includes("agriculture") ? {agriculture: {}} : {}})
} else if (mode === 'normal') {
itemInfo.value.rows.push({
id: uuidv4(),
mode: "normal",
quantity: 1,
inputPrice: 0,
price: 0,
taxPercent: taxPercentage,
discountPercent: 0,
unit: 1,
linkedEntitys: []
})
} else if (mode === 'service') {
let rowData = {
id: uuidv4(),
mode: "service",
quantity: 1,
inputPrice: 0,
price: 0,
taxPercent: taxPercentage,
discountPercent: 0,
unit: 1,
linkedEntitys: []
}
//Push Agriculture Holder only if Module is activated
itemInfo.value.rows.push({...rowData, ...auth.activeTenantData.extraModules.includes("agriculture") ? {agriculture: {}} : {}})
} else if (mode === "pagebreak") {
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: []
})
}
setPosNumbers()
}
const removePosition = (id) => {
itemInfo.value.rows = itemInfo.value.rows.filter(row => row.id !== id)
setPosNumbers()
}
const normalizeZip = (value) => String(value || "").replace(/\D/g, "")
const sanitizeAddressZipInput = () => {
itemInfo.value.address.zip = normalizeZip(itemInfo.value.address.zip)
}
const checkAddressZip = async () => {
const zip = normalizeZip(itemInfo.value.address.zip)
itemInfo.value.address.zip = zip
if (![4, 5].includes(zip.length)) return
const zipData = await useFunctions().useZipCheck(zip)
if (zipData?.zip) {
itemInfo.value.address.zip = zipData.zip
}
if (zipData?.short) {
itemInfo.value.address.city = zipData.short
}
}
const findDocumentErrors = computed(() => {
let errors = []
if (itemInfo.value.customer === null) errors.push({message: "Es ist kein Kunde ausgewählt", type: "breaking"})
if (itemInfo.value.contact === null) errors.push({message: "Es ist kein Kontakt ausgewählt", type: "info"})
if (itemInfo.value.letterhead === null) errors.push({message: "Es ist kein Briefpapier ausgewählt", type: "breaking"})
if (itemInfo.value.created_by === null || !itemInfo.value.created_by) errors.push({message: "Es ist kein Mitarbeiter ausgewählt", type: "breaking"})
if (!itemInfo.value.address.street) errors.push({
message: "Es ist keine Straße im Adressat angegeben",
type: "breaking"
})
if (!itemInfo.value.address.zip) errors.push({
message: "Es ist keine Postleitzahl im Adressat angegeben",
type: "breaking"
})
if (!itemInfo.value.address.city) errors.push({
message: "Es ist keine Stadt im Adressat angegeben",
type: "breaking"
})
if (itemInfo.value.project === null) errors.push({message: "Es ist kein Projekt ausgewählt", type: "info"})
if (itemInfo.value.plant === null) errors.push({message: "Es ist kein Objekt ausgewählt", type: "info"})
if (itemInfo.value.contract === null) errors.push({message: "Es ist kein Vertrag ausgewählt", type: "info"})
if (['Lieferzeitraum', 'Leistungszeitraum'].includes(itemInfo.value.deliveryDateType) && itemInfo.value.type !== "serialInvoices") {
if (itemInfo.value.deliveryDateEnd === null) errors.push({
message: `Es ist kein Enddatum für den ${itemInfo.value.deliveryDateType} angegeben`,
type: "breaking"
})
}
if (itemInfo.value.rows.length === 0) {
errors.push({message: "Es sind keine Positionen angegeben", type: "breaking"})
} else {
itemInfo.value.rows.forEach((row,index) => {
if (itemInfo.value.type !== "quotes" && row.optional) {
errors.push({
message: `Position ${row.pos} ist als Optional markiert. Dies wird nur in Angeboten unterstützt.`,
type: "breaking"
})
}
if (itemInfo.value.type !== "quotes" && row.alternative) {
errors.push({
message: `Position ${row.pos} ist als Alternativ markiert. Dies wird nur in Angeboten unterstützt.`,
type: "breaking"
})
}
if (row.mode === "normal" && !row.product) errors.push({
message: `In Position ${row.pos} ist kein Artikel ausgewählt`,
type: "breaking"
})
if (row.mode === "service" && !row.service) errors.push({
message: `In Position ${row.pos} ist keine Leistung ausgewählt`,
type: "breaking"
})
if (row.mode === "title" && !row.text) errors.push({
message: `In Position ${row.pos} ist kein Titel hinterlegt`,
type: "breaking"
})
//if(row.mode === "text" && !row.text) errors.push({message: `In einer Freitext Position ist kein Titel hinterlegt`, type: "breaking"})
if (row.mode === "free" && !row.text) errors.push({
message: `In einer freien Position ist kein Titel hinterlegt`,
type: "breaking"
})
if (["normal", "service", "free"].includes(row.mode)) {
if (!row.taxPercent && typeof row.taxPercent !== "number" && itemInfo.value.type !== "deliveryNotes") errors.push({
message: `In Position ${row.pos} ist kein Steuersatz hinterlegt`,
type: "breaking"
})
if (!row.price && typeof row.price !== "number" && itemInfo.value.type !== "deliveryNotes") errors.push({
message: `In Position ${row.pos} ist kein Preis hinterlegt`,
type: "breaking"
})
if (!row.unit) errors.push({message: `In Position ${row.pos} ist keine Einheit hinterlegt`, type: "breaking"})
}
if (row.agriculture) {
if (row.agriculture.dieselUsage && (!row.agriculture.dieselPrice && typeof row.agriculture.dieselPrice !== "number")) {
errors.push({message: `In Position ${row.pos} ist kein Dieselpreis hinterlegt`, type: "breaking"})
} else if (row.agriculture.dieselUsage && row.agriculture.dieselPrice === 0) {
errors.push({message: `In Position ${row.pos} ist 0,00 € als Dieselpreis hinterlegt`, type: "info"})
}
}
if (index === itemInfo.value.rows.length - 1 && row.mode === "pagebreak") {
errors.push({message: `Die letze Position darf kein Seitenumbruch sein`, type: "breaking"})
}
})
}
if (itemInfo.value.type === "serialInvoices") {
if (!itemInfo.value.serialConfig.intervall) errors.push({
message: `Kein Intervall für die Ausführung festgelegt`,
type: "breaking"
})
if (!itemInfo.value.serialConfig.dateDirection) errors.push({
message: `Kein Richtung für die Datierung festgelegt`,
type: "breaking"
})
if (!itemInfo.value.serialConfig.firstExecution) errors.push({
message: `Kein Datum für die erste Ausführung festgelegt`,
type: "breaking"
})
if (!itemInfo.value.serialConfig.executionUntil) errors.push({
message: `Kein Datum für die letzte Ausführung festgelegt`,
type: "info"
})
}
return errors.sort((a) => (a.type === "breaking") ? -1 : 1)
})
const tabItems = computed(() => {
return [
{
label: "Editor"
},
{
label: "Vorschau",
disabled: findDocumentErrors.value.filter(i => i.type === 'breaking').length > 0
}
]
})
const renderCurrency = (value, currency = "€") => {
//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)) {
let rowPrice = Number(Number(row.quantity) * Number(row.price) * (1 - Number(row.discountPercent) / 100)).toFixed(3)
totalNet = totalNet + Number(rowPrice)
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)
}
}
})
//Title Sum
let titleSums = {}
let titleSumsTransfer = {}
let lastTitle = ""
let transferCounter = 0
itemInfo.value.rows.forEach(row => {
if (row.mode === 'title') {
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)
let totalGross = Number(totalNet.toFixed(2)) + Number(total19.toFixed(2)) + Number(total7.toFixed(2))
let totalGrossAlreadyPaid = 0
itemInfo.value.usedAdvanceInvoices.forEach(advanceInvoiceId => {
let advanceInvoice = createddocuments.value.find(i => i.id === advanceInvoiceId)
let priceNet = advanceInvoice.rows.find(i => i.advanceInvoiceData).price
let partSum = priceNet * ((100 + advanceInvoice.rows.find(i => i.advanceInvoiceData).taxPercent) / 100)
totalGrossAlreadyPaid += partSum
})
console.log(totalGrossAlreadyPaid)
let sumToPay = 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
}
})
const documentReport = computed(() => {
let totalProductsPurchasePrice = 0
let totalProductsFromServicesPurchasePrice = 0
let totalHoursFromServices = {
total: 0,
totalPurchasePrice: 0,
byName: {}
}
let totalHoursSellingPrice = 0
itemInfo.value.rows.filter(i => !i.optional && !i.alternative).forEach(row => {
if (row.product) {
let product = products.value.find(i => i.id === row.product)
totalProductsPurchasePrice += (product?.purchase_price || 0) * row.quantity
} else if (row.service) {
let service = services.value.find(i => i.id === row.service)
if (service.materialComposition) {
service.materialComposition.forEach(entry => {
let productData = products.value.find(i => i.id === entry.product)
totalProductsFromServicesPurchasePrice += (productData?.purchase_price || 0) * entry.quantity * row.quantity
})
}
if (service.personalComposition) {
service.personalComposition.forEach(entry => {
totalHoursFromServices.total += entry.quantity * row.quantity
totalHoursFromServices.totalPurchasePrice += entry.quantity * entry.purchasePrice * row.quantity
totalHoursSellingPrice += entry.quantity * entry.price * row.quantity
if (totalHoursFromServices.byName[entry.name]) {
totalHoursFromServices.byName[entry.name] += entry.quantity * row.quantity
} else {
totalHoursFromServices.byName[entry.name] = entry.quantity * row.quantity
}
})
} else {
//totalHoursSellingPrice += service.sellingPriceComposed.totalWorker * row.quantity
}
//totalProductsPurchasePrice += product.purchasePrice * row.quantity
}
})
let totalMargin = documentTotal.value.totalNet - totalProductsPurchasePrice - totalProductsFromServicesPurchasePrice - totalHoursFromServices.totalPurchasePrice
return {
totalProductsPurchasePrice,
totalProductsFromServicesPurchasePrice,
totalMargin,
totalHoursFromServices,
totalHoursSellingPrice,
}
})
const processDieselPosition = () => {
let agricultureData = {
dieselUsageTotal: 0,
dieselPriceTotal: 0,
adblueUsageTotal: 0,
adbluePriceTotal: 0
}
itemInfo.value.rows.forEach(row => {
if (row.agriculture && row.agriculture.dieselUsage) {
console.log(row.agriculture)
agricultureData.dieselUsageTotal += Number(row.agriculture.dieselUsage)
agricultureData.dieselPriceTotal += Number(row.agriculture.dieselPrice || 0) * Number(row.agriculture.dieselUsage)
agricultureData.adblueUsageTotal += Number(row.agriculture.adblueUsage || 0)
agricultureData.adbluePriceTotal += Number(row.agriculture.adbluePrice || 0) * Number(row.agriculture.adblueUsage || 0)
}
})
console.log(agricultureData)
if (agricultureData.dieselUsageTotal !== 0) {
if (itemInfo.value.rows.find(i => i.key === "dieselPos")) {
let existingIndex = itemInfo.value.rows.findIndex(i => i.key === "dieselPos")
itemInfo.value.rows[existingIndex] = {
...itemInfo.value.rows[existingIndex],
price: agricultureData.dieselPriceTotal,
text: `${agricultureData.dieselUsageTotal} L Diesel`,
}
let existingAdblueIndex = itemInfo.value.rows.findIndex(i => i.key === "adbluePos")
itemInfo.value.rows[existingAdblueIndex] = {
...itemInfo.value.rows[existingAdblueIndex],
price: agricultureData.adbluePriceTotal,
text: `${agricultureData.adblueUsageTotal} L AdBlue`,
}
} else {
itemInfo.value.rows.push({
mode: "free",
text: `${(agricultureData.dieselUsageTotal).toFixed(2).replace(".", ",")} L Diesel`,
quantity: 1,
unit: 10,
price: agricultureData.dieselPriceTotal,
taxPercent: 19,
discountPercent: 0,
key: "dieselPos"
})
itemInfo.value.rows.push({
mode: "free",
text: `${(agricultureData.adblueUsageTotal).toFixed(2).replace(".", ",")} L AdBlue`,
quantity: 1,
unit: 10,
price: agricultureData.adbluePriceTotal,
taxPercent: 19,
discountPercent: 0,
key: "adbluePos"
})
setPosNumbers()
}
}
itemInfo.value.agriculture = {...itemInfo.value.agriculture, ...agricultureData}
}
const getDocumentData = async () => {
let customerData = customers.value.find(i => i.id === itemInfo.value.customer)
let contactData = contacts.value.find(i => i.id === itemInfo.value.contact)
let businessInfo = auth.activeTenantData.businessInfo
if (auth.activeTenantData.extraModules.includes("agriculture")) {
itemInfo.value.rows.forEach(row => {
if (row.agriculture && row.agriculture.dieselUsage) {
row.agriculture.description = `${row.agriculture.dieselUsage} L Diesel zu ${renderCurrency(row.agriculture.dieselPrice)}/L verbraucht ${row.description ? "\n" + row.description : ""}`
}
})
}
let rows = itemInfo.value.rows
if (itemInfo.value.taxType === "13b UStG" || itemInfo.value.taxType === "19 UStG") {
rows = rows.map(row => {
return {
...row,
taxPercent: 0
}
})
}
rows = itemInfo.value.rows.map(row => {
let unit = units.value.find(i => i.id === row.unit)
if (!['pagebreak', 'title'].includes(row.mode)) {
if (row.agriculture && row.agriculture.description) {
console.log("Row has Agri")
row.descriptionText = row.agriculture.description
} else if (row.description) {
console.log("Row has no Agri")
row.descriptionText = row.description
} else {
delete row.descriptionText
}
}
if (!['pagebreak', 'title', 'text'].includes(row.mode)) {
if (row.mode === 'normal') row.text = products.value.find(i => i.id === row.product).name
if (row.mode === 'service') row.text = services.value.find(i => i.id === row.service).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
}
})
//Compile Start & EndText
const templateStartText = Handlebars.compile(itemInfo.value.startText);
const templateEndText = Handlebars.compile(itemInfo.value.endText);
const generateContext = (itemInfo, contactData) => {
return {
lohnkosten: documentReport.value.totalHoursSellingPrice ? useCurrency(documentReport.value.totalHoursSellingPrice) : null,
anrede: (contactData && contactData.salutation) || (customerData && customerData.salutation),
titel: (contactData && contactData.title) || (customerData && customerData.title),
vorname: (contactData && contactData.firstName) || (customerData && customerData.firstname),
nachname: (contactData && contactData.lastName) || (customerData && customerData.lastname),
kundenname: customerData && customerData.name,
zahlungsziel_in_tagen: itemInfo.paymentDays,
zahlungsart: itemInfo.payment_type === "transfer" ? "Überweisung" : "Lastschrift",
diesel_gesamtverbrauch: (itemInfo.agriculture && itemInfo.agriculture.dieselUsageTotal) && itemInfo.agriculture.dieselUsageTotal
}
}
let contactPerson = (await useNuxtApp().$api(`/api/user/${itemInfo.value.created_by}`, {
method: "GET"
})).profile
let returnTitleSums = {}
if (Object.keys(documentTotal.value.titleSums).length > 0) {
Object.keys(documentTotal.value.titleSums).forEach(key => {
returnTitleSums[key] = renderCurrency(documentTotal.value.titleSums[key])
})
}
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 = {
type: itemInfo.value.type,
taxType: itemInfo.value.taxType,
adressLine: `${businessInfo.name}, ${businessInfo.street}, ${businessInfo.zip} ${businessInfo.city}`,
/*recipient: {
name: customerData.name,
contact: contactData ? `${contactData.firstName} ${contactData.lastName}` : "",
street: itemInfo.value.address.street || customerData.infoData.street,
special: itemInfo.value.address.special || customerData.infoData.special,
city: itemInfo.value.address.city || customerData.infoData.city,
zip: itemInfo.value.address.zip || customerData.infoData.zip
},*/
recipient: [
customerData.name,
...customerData.nameAddition ? [customerData.nameAddition] : [],
...contactData ? [`${contactData.firstName} ${contactData.lastName}`] : [],
itemInfo.value.address.street,
...itemInfo.value.address.special ? [itemInfo.value.address.special] : [],
`${itemInfo.value.address.zip} ${itemInfo.value.address.city}`,
],
/*info: {
customerNumber: customerData.customerNumber,
documentNumber: itemInfo.value.documentNumber,
documentNumberTitle: itemInfo.value.documentNumberTitle,
documentDate: dayjs(itemInfo.value.documentDate).format("DD.MM.YYYY"),
deliveryDate: dayjs(itemInfo.value.deliveryDate).format("DD.MM.YYYY"),
deliveryDateEnd: itemInfo.value.deliveryDateEnd ? dayjs(itemInfo.value.deliveryDateEnd).format("DD.MM.YYYY") : null,
deliveryDateType: itemInfo.value.deliveryDateType,
contactPerson: contactPerson.fullName,
contactTel: contactPerson.fixedTel || contactPerson.mobileTel,
contactEMail: contactPerson.email,
project: projects.value.find(i => i.id === itemInfo.value.project) ? projects.value.find(i => i.id === itemInfo.value.project).name : null,
plant: plants.value.find(i => i.id === itemInfo.value.plant) ? plants.value.find(i => i.id === itemInfo.value.plants).name : null,
},*/
info: [
{
label: itemInfo.value.documentNumberTitle,
content: itemInfo.value.documentNumber || "XXXX",
}, {
label: "Kundennummer",
content: customerData.customerNumber,
}, {
label: "Belegdatum",
content: itemInfo.value.documentDate ? dayjs(itemInfo.value.documentDate).format("DD.MM.YYYY") : "XXXX",
},
...itemInfo.value.deliveryDateType !== "Kein Lieferdatum anzeigen" ? [{
label: itemInfo.value.deliveryDateType,
content: !['Lieferzeitraum', 'Leistungszeitraum'].includes(itemInfo.value.deliveryDateType) ? (itemInfo.value.deliveryDate ? dayjs(itemInfo.value.deliveryDate).format("DD.MM.YYYY") : "XXXX") : `${itemInfo.value.deliveryDate ? dayjs(itemInfo.value.deliveryDate).format("DD.MM.YYYY") : "XXXX"} - ${itemInfo.value.deliveryDateEnd ? dayjs(itemInfo.value.deliveryDateEnd).format("DD.MM.YYYY") : "XXXX"}`,
}] : [],
/*{
label: itemInfo.value.deliveryDateType,
content: !['Lieferzeitraum','Leistungszeitraum'].includes(itemInfo.value.deliveryDateType) ? dayjs(itemInfo.value.deliveryDate).format("DD.MM.YYYY") : `${dayjs(itemInfo.value.deliveryDate).format("DD.MM.YYYY")} - ${dayjs(itemInfo.value.deliveryDateEnd).format("DD.MM.YYYY")}`,
},*/
{
label: "Ansprechpartner",
content: contactPerson.full_name,
},
...itemInfo.value.contactTel || contactPerson.fixed_tel || contactPerson.mobile_tel ? [{
label: "Telefon",
content: itemInfo.value.contactTel || contactPerson.fixed_tel || contactPerson.mobile_tel,
}] : [],
...contactPerson.email ? [{
label: "E-Mail",
content: contactPerson.email,
}] : [],
...itemInfo.value.plant ? [{
label: "Objekt",
content: plants.value.find(i => i.id === itemInfo.value.plant).name,
}] : [],
...itemInfo.value.project ? [{
label: "Projekt",
content: projects.value.find(i => i.id === itemInfo.value.project).name
}] : [],
...itemInfo.value.contract ? [{
label: "Vertrag",
content: contracts.value.find(i => i.id === itemInfo.value.contract).contractNumber
}] : []
],
title: itemInfo.value.title,
description: itemInfo.value.description,
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) && !["13b UStG"].includes(itemInfo.value.taxType) ? [{
label: `zzgl. 19% USt auf ${renderCurrency(documentTotal.value.totalNet19)}`,
content: renderCurrency(documentTotal.value.total19),
}] : [],
...rows.find(i => i.taxPercent === 7) && !["13b UStG"].includes(itemInfo.value.taxType) ? [{
label: `zzgl. 7% USt auf ${renderCurrency(documentTotal.value.totalNet7)}`,
content: renderCurrency(documentTotal.value.total7),
}] : [],
...rows.find(i => i.taxPercent === 0) && !["13b UStG"].includes(itemInfo.value.taxType) ? [{
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,
titleSumsTransfer: returnTitleSumsTransfer
},
agriculture: itemInfo.value.agriculture,
usedAdvanceInvoices: itemInfo.value.usedAdvanceInvoices.map(i => {
return createddocuments.value.find(x => x.id === i)
})
}
console.log(returnData)
return returnData
}
const showDocument = ref(false)
const uri = ref("")
const generateDocument = async () => {
showDocument.value = false
const path = letterheads.value.find(i => i.id === itemInfo.value.letterhead).path
uri.value = await useFunctions().useCreatePDF(await getDocumentData(), path, "createdDocument")
/*uri.value = await useNuxtApp().$api("/api/functions/createinvoicepdf",{
method: "POST",
body: {
invoiceData: await getDocumentData(),
backgroundPath: path
}
})*/
showDocument.value = true
}
const onChangeTab = (index) => {
if (index === 1) {
generateDocument()
}
}
const setPosNumbers = () => {
let mainIndex = 1
let subIndex = 1
let rows = itemInfo.value.rows.map(row => {
if (row.mode === 'title') {
row.pos = mainIndex
mainIndex += 1
subIndex = 1
} else if (!['pagebreak', 'title', 'text'].includes(row.mode)) {
row.pos = itemInfo.value.rows.filter(i => i.mode === "title").length === 0 ? `${subIndex}` : `${mainIndex - 1}.${subIndex}`
subIndex += 1
}
if (!row.id) {
row.id = uuidv4()
}
return row
})
}
const saveSerialInvoice = async () => {
let createData = {
type: itemInfo.value.type,
state: 'Erstellt',
customer: itemInfo.value.customer,
contact: itemInfo.value.contact,
contract: itemInfo.value.contract,
address: itemInfo.value.address,
project: itemInfo.value.project,
paymentDays: itemInfo.value.paymentDays,
payment_type: itemInfo.value.payment_type,
deliveryDateType: "Leistungszeitraum",
createdBy: itemInfo.value.createdBy,
created_by: itemInfo.value.created_by,
title: itemInfo.value.title,
description: itemInfo.value.description,
startText: itemInfo.value.startText,
endText: itemInfo.value.endText,
rows: itemInfo.value.rows,
contactPerson: itemInfo.value.contactPerson,
serialConfig: itemInfo.value.serialConfig,
letterhead: itemInfo.value.letterhead,
taxType:itemInfo.value.taxType
}
let data = null
if (route.params.id) {
data = await useEntities("createddocuments").update(route.params.id, createData)
} else {
data = await useEntities("createddocuments").create(createData)
}
await router.push(`/createDocument/edit/${data.id}`)
}
const saveDocument = async (state, resetup = false) => {
itemInfo.value.state = state
if (state !== "Entwurf") {
console.log("???")
let type = ""
if (itemInfo.value.type === "advanceInvoices" || itemInfo.value.type === "cancellationInvoices") {
type = "invoices"
} else {
type = itemInfo.value.type
}
try {
itemInfo.value.documentNumber = await useFunctions().useNextNumber(type) //data.usedNumber
setDocumentTypeConfig(false)
} catch {
console.log("Error Happend")
}
}
if (auth.activeTenantData.extraModules.includes("agriculture")) {
itemInfo.value.rows.forEach(row => {
if (row.agriculture && row.agriculture.dieselUsage) {
row.agriculture.description = `${row.agriculture.dieselUsage} L Diesel zu ${renderCurrency(row.agriculture.dieselPrice)}/L verbraucht ${row.description ? "\n" + row.description : ""}`
}
})
}
//Check if Agricultural Description is Present
itemInfo.value.rows = itemInfo.value.rows.map(row => {
let descriptionText = ""
if (row.agriculture && row.agriculture.description) {
descriptionText = row.agriculture.description
} else {
descriptionText = row.description ? row.description : null
}
return {
...row,
descriptionText: descriptionText
}
})
let createData = {
type: itemInfo.value.type,
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,
contract: itemInfo.value.contract,
address: itemInfo.value.address,
project: itemInfo.value.project,
plant: itemInfo.value.plant,
documentNumber: itemInfo.value.documentNumber,
documentDate: itemInfo.value.documentDate,
deliveryDate: itemInfo.value.deliveryDate,
deliveryDateEnd: itemInfo.value.deliveryDateEnd,
paymentDays: itemInfo.value.paymentDays,
payment_type: itemInfo.value.payment_type,
deliveryDateType: itemInfo.value.deliveryDateType,
info: {},
createdBy: itemInfo.value.createdBy,
created_by: itemInfo.value.created_by,
title: itemInfo.value.title,
description: itemInfo.value.description,
startText: itemInfo.value.startText,
endText: itemInfo.value.endText,
rows: itemInfo.value.rows,
contactPerson: itemInfo.value.contactPerson,
createddocument: itemInfo.value.createddocument,
agriculture: itemInfo.value.agriculture,
letterhead: itemInfo.value.letterhead,
usedAdvanceInvoices: itemInfo.value.usedAdvanceInvoices,
customSurchargePercentage: itemInfo.value.customSurchargePercentage,
report: documentReport.value
}
if (route.params.id) {
//await dataStore.updateItem("createddocuments", {...createData, id: itemInfo.value.id})
await useEntities("createddocuments").update(itemInfo.value.id, {...createData, id: itemInfo.value.id})
} else {
const data = await useEntities("createddocuments").create(createData)
console.log(data)
await router.push(`/createDocument/edit/${data.id}`)
}
if (resetup) await setupPage()
}
const selectedTab = ref(0)
const closeDocument = async () => {
if(selectedTab.value === 0) {
await generateDocument()
selectedTab.value = 1
} else {
loaded.value = false
await saveDocument("Gebucht")
await generateDocument()
let fileData = {}
fileData.project = itemInfo.value.project
fileData.createddocument = itemInfo.value.id
let mappedType = itemInfo.value.type
if (mappedType === "advanceInvoices" || mappedType === "cancellationInvoices") {
mappedType = "invoices"
}
const folders = await useEntities("folders").select()
console.log(folders)
fileData.folder = folders.find(i => i.function === mappedType && i.year === Number(dayjs().format("YYYY"))).id
const tags = await useEntities("filetags").select()
console.log(tags)
fileData.type = tags.find(i => i.createdDocumentType === mappedType).id
function dataURLtoFile(dataurl, filename) {
var arr = dataurl.split(","),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[arr.length - 1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, {type: mime});
}
let file = dataURLtoFile(uri.value, `${itemInfo.value.documentNumber}.pdf`)
await useFiles().uploadFiles(fileData, [file])
guard.value = false
await router.push(`/createDocument/show/${itemInfo.value.id}`)
}
}
const getTextTemplateByType = (type, pos) => {
let finalType = type
if (type === "serialInvoices") {
finalType = "invoices"
}
if (pos) {
return texttemplates.value.filter(i => i.documentType === finalType && i.pos === pos)
} else {
return texttemplates.value.filter(i => i.documentType === finalType)
}
}
const checkCompatibilityWithInputPrice = () => {
itemInfo.value.rows.forEach(row => {
if (!row.inputPrice) {
row.inputPrice = row.price
}
})
}
const updateCustomSurcharge = () => {
itemInfo.value.rows.forEach(row => {
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
}*/
})
}
const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {}) => {
console.log("Set Row Data")
if (service && service.id) {
row.service = service.id
services.value = await useEntities("services").select("*")
}
if (product && product.id) {
row.product = product.id
products.value = await useEntities("products").select("*")
}
if (row.service) {
console.log(service)
if(service.unit) {
row.unit = service.unit
} else {
let selectedService = services.value.find(i => i.id === row.service)
console.log(selectedService)
if(selectedService.unit?.id) {
row.unit = selectedService.unit.id
} else {
row.unit = selectedService.unit
}
}
//row.unit = service.unit ? service.unit : services.value.find(i => i.id === row.service).unit
row.inputPrice = ((service.sellingPriceComposed.total || service.sellingPrice) ? (service.sellingPriceComposed.total || service.sellingPrice) : (services.value.find(i => i.id === row.service).sellingPriceComposed.total || services.value.find(i => i.id === row.service).sellingPrice))
row.description = service.description ? service.description : (services.value.find(i => i.id === row.service) ? services.value.find(i => i.id === row.service).description : "")
if (['13b UStG', '19 UStG'].includes(itemInfo.value.taxType)) {
row.taxPercent = 0
} else {
row.taxPercent = service.taxPercentage ? service.taxPercentage : services.value.find(i => i.id === row.service).taxPercentage
}
}
if (row.product) {
console.log("Product Detected")
row.unit = product.unit ? product.unit : products.value.find(i => i.id === row.product).unit
row.inputPrice = (product.selling_price ? product.selling_price : products.value.find(i => i.id === row.product).selling_price)
//row.price = Number((row.originalPrice * (1 + itemInfo.value.customSurchargePercentage /100)).toFixed(2))
row.description = product.description ? product.description : (products.value.find(i => i.id === row.product) ? products.value.find(i => i.id === row.product).description : "")
if (['13b UStG', '19 UStG'].includes(itemInfo.value.taxType)) {
row.taxPercent = 0
} else {
row.taxPercent = product.tax_percentage ? product.tax_percentage : products.value.find(i => i.id === row.product).tax_percentage
}
}
updateCustomSurcharge()
}
</script>
<template>
<UDashboardNavbar>
<template #left>
<UButton
icon="i-heroicons-chevron-left"
variant="outline"
@click="router.back()/*router.push(`/standardEntity/${type}`)*/"
>
<!-- {{dataType.label}}-->
</UButton>
</template>
<template #right>
<ArchiveButton
color="rose"
type="createddocuments"
v-if="itemInfo.state === 'Entwurf' || itemInfo.type === 'serialInvoices'"
variant="outline"
@confirmed="useEntities('createddocuments').update(itemInfo.id,{archived: true}),
router.push('/')"
/>
<UButton
icon="i-mdi-content-save"
@click="saveDocument('Entwurf',true)"
v-if="itemInfo.type !== 'serialInvoices' "
:disabled="!itemInfo.customer"
>
Speichern
</UButton>
<UButton
@click="closeDocument"
v-if="itemInfo.id && itemInfo.type !== 'serialInvoices'"
>
{{selectedTab === 0 ? "Vorschau zeigen" : "Fertigstellen"}}
</UButton>
<UButton
icon="i-mdi-content-save"
@click="saveSerialInvoice"
v-if="itemInfo.type === 'serialInvoices'"
>
Serienrechnung
</UButton>
</template>
</UDashboardNavbar>
<UDashboardPanelContent>
<UTabs class="p-5" :items="tabItems" @change="onChangeTab" v-if="loaded" v-model="selectedTab">
<template #item="{item}">
<div v-if="item.label === 'Editor'">
<UAlert
class="my-5"
title="Vorhandene Probleme und Informationen:"
:color="findDocumentErrors.filter(i => i.type === 'breaking').length > 0 ? 'rose' : 'white'"
variant="outline"
v-if="findDocumentErrors.length > 0"
>
<template #description>
<ul class="list-disc ml-5">
<li v-for="error in findDocumentErrors"
:class="[...error.type === 'breaking' ? ['text-rose-600'] : ['dark:text-white','text-black']]">
{{ error.message }}
</li>
</ul>
</template>
</UAlert>
<InputGroup>
<div class="w-1/3 mr-5">
<UFormGroup
label="Dokumenttyp:"
>
<InputGroup>
<USelectMenu
:options="Object.keys(dataStore.documentTypesForCreation).map(i => {return {label: dataStore.documentTypesForCreation[i].labelSingle, type: i }})"
v-model="itemInfo.type"
value-attribute="type"
option-attribute="label"
@change="setDocumentTypeConfig"
class="flex-auto"
>
<template #label>
{{ dataStore.documentTypesForCreation[itemInfo.type].labelSingle }}
</template>
</USelectMenu>
<!-- <UButton
variant="outline"
v-if="itemInfo.type === 'advanceInvoices'"
icon="i-heroicons-arrow-down-tray"
@click="showAdvanceInvoiceCalcModal = true"
>Auto Positionen</UButton>-->
</InputGroup>
<USlideover
v-model="showAdvanceInvoiceCalcModal"
>
<UCard class="h-full">
<template #header>
<UButton @click="importPositions">Übernehmen</UButton>
</template>
<UFormGroup
label="Gesamtsumme:"
>
<UInput
type="number"
:step="0.01"
v-model="advanceInvoiceData.totalSumNet"
@focusout="advanceInvoiceData.part = advanceInvoiceData.totalSumNet / 100 * advanceInvoiceData.partPerPecentage"
/>
</UFormGroup>
<UFormGroup
label="Prozent:"
>
<UInput
type="number"
:step="0.01"
v-model="advanceInvoiceData.partPerPecentage"
@focusout="advanceInvoiceData.part = advanceInvoiceData.totalSumNet / 100 * advanceInvoiceData.partPerPecentage"
/>
</UFormGroup>
<UFormGroup
label="Abzurechnender Anteil:"
>
<UInput
type="number"
:step="0.01"
v-model="advanceInvoiceData.part"
@focusout="advanceInvoiceData.partPerPecentage = Number((advanceInvoiceData.part / advanceInvoiceData.totalSumNet * 100).toFixed(2))"
/>
</UFormGroup>
</UCard>
</USlideover>
</UFormGroup>
<UFormGroup
label="Steuertyp:"
v-if="['invoices','advanceInvoices','quotes','confirmationOrders','serialInvoices'].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'}*/]"
v-model="itemInfo.taxType"
value-attribute="key"
option-attribute="label"
@change="setTaxType"
class="w-full"
></USelectMenu>
</UFormGroup>
<UFormGroup
label="Briefpapier:"
>
<USelectMenu
:options="letterheads"
v-model="itemInfo.letterhead"
value-attribute="id"
option-attribute="name"
searchable
searchable-placeholder="Suche..."
:search-attributes="['name']"
class="w-full"
:color="itemInfo.letterhead ? 'primary' : 'rose'"
>
<template #label>
{{ itemInfo.letterhead ? letterheads.find(i => i.id === itemInfo.letterhead).name : "Kein Briefpapier gewählt" }}
</template>
</USelectMenu>
</UFormGroup>
<UFormGroup
label="Kunde:"
>
<div class="flex flex-row">
<USelectMenu
:options="customers"
option-attribute="name"
value-attribute="id"
:search-attributes="['name']"
searchable
searchable-placeholder="Suche..."
v-model="itemInfo.customer"
@change="setCustomerData"
class="flex-auto"
>
<template #option="{option}">
{{ option.name }}{{ option.nameAddition }}
</template>
<UButton
:color="itemInfo.customer ? 'primary' : 'rose'"
variant="outline"
class="w-full"
>
{{ customers.find(i => i.id === itemInfo.customer) ? customers.find(i => i.id === itemInfo.customer).name : "Kein Kunde ausgewählt" }}
<UIcon name="i-heroicons-chevron-right-20-solid"
class="w-5 h-5 transition-transform text-gray-400 dark:text-gray-500"
:class="['transform rotate-90']"/>
</UButton>
</USelectMenu>
<EntityModalButtons
type="customers"
:id="itemInfo.customer"
@return-data="(data) => setCustomerData(data.id)"
/>
<!-- <UButton
variant="outline"
class="w-25 ml-2"
v-if="itemInfo.customer"
icon="i-heroicons-eye"
@click="modal.open(StandardEntityModal, {
id: itemInfo.customer,
type: 'customers',
mode: 'show',
})"
/>
<UButton
variant="outline"
class="w-25 ml-2"
v-if="itemInfo.customer"
icon="i-heroicons-pencil-solid"
@click="modal.open(StandardEntityModal, {
id: itemInfo.customer,
type: 'customers',
mode: 'edit',
onReturnData(data) {
setCustomerData()
}
})"
/>
<UButton
variant="outline"
class="w-25 ml-2"
v-if="!itemInfo.customer"
icon="i-heroicons-plus"
@click="modal.open(StandardEntityModal, {
type: 'customers',
mode: 'create',
onReturnData(data) {
setCustomerData(data.id)
}
})"
/>-->
</div>
<UAlert
v-if="customers.find(i => i.id === itemInfo.customer)"
class="mt-2"
variant="solid"
color="white"
title="Info"
>
<template #description>
Kundennr: {{ customers.find(i => i.id === itemInfo.customer).customerNumber }}<br>
Typ: {{ customers.find(i => i.id === itemInfo.customer).isCompany ? 'Firma' : 'Privat' }}<br>
Notizen: <span class="truncate">{{ customers.find(i => i.id === itemInfo.customer).notes }}</span>
</template>
</UAlert>
</UFormGroup>
<UFormGroup
label="Ansprechpartner:"
v-if="itemInfo.customer ? customers.find(i => i.id === itemInfo.customer).isCompany : false "
>
<InputGroup>
<USelectMenu
:options="contacts.filter(i => i.customer?.id === itemInfo.customer)"
option-attribute="fullName"
value-attribute="id"
:search-attributes="['name']"
searchable
searchable-placeholder="Suche..."
v-model="itemInfo.contact"
class="flex-auto"
>
<UButton
:color="itemInfo.contact ? 'primary' : 'none'"
variant="outline"
class="w-full"
:disabled="!itemInfo.customer"
>
<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>
{{ itemInfo.contact ? contacts.find(i => i.id === itemInfo.contact).fullName : "Kein Kontakt ausgewählt" }}
</template>
</USelectMenu>
<!-- <UButton
variant="outline"
v-if="itemInfo.contact"
icon="i-heroicons-arrow-right-end-on-rectangle"
@click="router.push(`/standardEntity/contacts/show/${itemInfo.contact}`)"
>Kontakt</UButton>-->
<UButton
variant="outline"
color="rose"
v-if="itemInfo.contact"
icon="i-heroicons-x-mark"
@click="itemInfo.contact = null"
/>
<EntityModalButtons
type="contacts"
:id="itemInfo.contact"
:create-query="{customer: itemInfo.customer}"
@return-data="(data) => itemInfo.contact = data.id"
/>
</InputGroup>
</UFormGroup>
<UFormGroup
label="Adresse:"
>
<UInput
v-model="itemInfo.address.street"
: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="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"
type="text"
inputmode="numeric"
maxlength="5"
@input="sanitizeAddressZipInput"
@change="checkAddressZip"
: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="itemInfo.customer ? customers.find(i => i.id === itemInfo.customer).infoData.city : 'Ort'"
:color="itemInfo.address.city ? 'primary' : 'rose'"
/>
</InputGroup>
</UFormGroup>
</div>
<div class="w-2/3">
<UFormGroup
:label="itemInfo.documentNumberTitle + ':'"
v-if="itemInfo.type !== 'serialInvoices'"
>
<UInput
v-model="itemInfo.documentNumber"
placeholder="XXXX"
disabled
/>
</UFormGroup>
<InputGroup class="w-full">
<UFormGroup
class="w-80 mr-1"
label="Lieferdatumsart:"
v-if="itemInfo.type !== 'serialInvoices'"
>
<USelectMenu
:options="['Lieferdatum','Lieferzeitraum','Leistungsdatum','Leistungszeitraum','Kein Lieferdatum anzeigen']"
v-model="itemInfo.deliveryDateType"
/>
</UFormGroup>
<UFormGroup
:label="`${itemInfo.deliveryDateType}${['Lieferzeitraum', 'Leistungszeitraum'].includes(itemInfo.deliveryDateType) ? ' Start' : ''}:`"
v-if="itemInfo.type !== 'serialInvoices' && itemInfo.deliveryDateType !== 'Kein Lieferdatum anzeigen'"
class="mr-1"
>
<UPopover :popper="{ placement: 'bottom-start' }">
<UButton
icon="i-heroicons-calendar-days-20-solid"
:label="itemInfo.deliveryDate ? dayjs(itemInfo.deliveryDate).format('DD.MM.YYYY') : 'Datum auswählen'"
variant="outline"
class="mx-auto"
/>
<template #panel="{ close }">
<LazyDatePicker v-model="itemInfo.deliveryDate" @close="close"/>
</template>
</UPopover>
</UFormGroup>
<UFormGroup
:label="itemInfo.deliveryDateType + ' Ende:'"
v-if="itemInfo.type !== 'serialInvoices' && ['Lieferzeitraum','Leistungszeitraum'].includes(itemInfo.deliveryDateType)"
>
<UPopover
:popper="{ placement: 'bottom-start' }">
<UButton
icon="i-heroicons-calendar-days-20-solid"
:label="itemInfo.deliveryDateEnd ? dayjs(itemInfo.deliveryDateEnd).format('DD.MM.YYYY') : 'Datum auswählen'"
variant="outline"
class="mx-auto"
/>
<template #panel="{ close }">
<LazyDatePicker v-model="itemInfo.deliveryDateEnd" @close="close"/>
</template>
</UPopover>
</UFormGroup>
</InputGroup>
<InputGroup class="w-full">
<UFormGroup
label="Belegdatum:"
class="mr-1"
v-if="itemInfo.type !== 'serialInvoices'"
>
<UPopover :popper="{ placement: 'bottom-start' }">
<UButton
icon="i-heroicons-calendar-days-20-solid"
:label="itemInfo.documentDate ? dayjs(itemInfo.documentDate).format('DD.MM.YYYY') : 'Datum auswählen'"
variant="outline"
/>
<template #panel="{ close }">
<LazyDatePicker v-model="itemInfo.documentDate" @close="close"/>
</template>
</UPopover>
</UFormGroup>
<UFormGroup
class="w-full"
label="Zahlungsziel in Tagen:"
>
<UInput
type="number"
v-model="itemInfo.paymentDays"
/>
</UFormGroup>
<UFormGroup
class="w-full"
label="Zahlungsart:"
>
<USelectMenu
v-model="itemInfo.payment_type"
value-attribute="key"
option-attribute="label"
:options="[
{
key: 'transfer',
label: 'Überweisung'
},{
key: 'direct-debit',
label: 'SEPA Lastschrift'
}
]"
>
<template #label>
{{itemInfo.payment_type === 'transfer' ? "Überweisung" : "SEPA-Lastschrift"}}
</template>
</USelectMenu>
</UFormGroup>
<UFormGroup
class="w-full"
label="Individueller Aufschlag:"
>
<UInput
type="number"
step="0.01"
v-model="itemInfo.customSurchargePercentage"
@change="updateCustomSurcharge"
>
<template #trailing>
<span class="text-gray-500 dark:text-gray-400 text-xs">%</span>
</template>
</UInput>
</UFormGroup>
</InputGroup>
<UFormGroup
label="Mitarbeiter:"
>
<USelectMenu
:options="tenantUsers"
v-model="itemInfo.created_by"
option-attribute="full_name"
value-attribute="id"
@change="setContactPersonData"
/>
</UFormGroup>
<UFormGroup
label="Kontakt Telefon:"
>
<UInput
v-model="itemInfo.contactTel"
/>
</UFormGroup>
<UFormGroup
label="Kontakt E-Mail:"
>
<UInput
v-model="itemInfo.contactEMail"
/>
</UFormGroup>
<UFormGroup
label="Objekt:"
>
<InputGroup>
<USelectMenu
:options="plants.filter(i => i.customer?.id === itemInfo.customer)"
v-model="itemInfo.plant"
value-attribute="id"
option-attribute="name"
searchable
searchable-placeholder="Suche..."
:search-attributes="['name']"
class="w-full"
:disabled="!itemInfo.customer"
>
<template #label>
{{ plants.find(i => i.id === itemInfo.plant) ? plants.find(i => i.id === itemInfo.plant).name : "Kein Objekt ausgewählt" }}
</template>
<template #option="{option: plant}">
{{ plant.name }}
</template>
</USelectMenu>
<UButton
variant="outline"
color="rose"
v-if="itemInfo.plant"
icon="i-heroicons-x-mark"
@click="itemInfo.plant = null"
/>
<EntityModalButtons
type="plants"
:id="itemInfo.plant"
:create-query="{customer: itemInfo.customer}"
@return-data="(data) => itemInfo.plant = data.id"
/>
</InputGroup>
</UFormGroup>
<UFormGroup
label="Projekt:"
>
<InputGroup>
<USelectMenu
:options="projects.filter(i => i.customer?.id === itemInfo.customer && (itemInfo.plant ? itemInfo.plant === i.plant : true))"
v-model="itemInfo.project"
value-attribute="id"
option-attribute="name"
searchable
searchable-placeholder="Suche..."
:search-attributes="['name']"
class="w-full"
:disabled="!itemInfo.customer"
>
<template #label>
{{ itemInfo.project ? projects.find(i => i.id === itemInfo.project).name : "Kein Projekt ausgewählt" }}
</template>
<template #option="{option: project}">
{{ customers.find(i => i.id === project.customer) ? customers.find(i => i.id === project.customer).name : "" }}
- {{ project.name }}
</template>
</USelectMenu>
<UButton
variant="outline"
color="rose"
v-if="itemInfo.project"
icon="i-heroicons-x-mark"
@click="itemInfo.project = null"
/>
<EntityModalButtons
type="projects"
:id="itemInfo.project"
:create-query="{customer: itemInfo.customer, plant: itemInfo.plant}"
@return-data="(data) => itemInfo.project = data.id"
/>
</InputGroup>
</UFormGroup>
<UFormGroup
label="Vertrag:"
>
<InputGroup>
<USelectMenu
:options="contracts.filter(i => i.customer?.id === itemInfo.customer && (itemInfo.plant ? itemInfo.plant === i.plant : true))"
v-model="itemInfo.contract"
value-attribute="id"
option-attribute="name"
searchable
searchable-placeholder="Suche..."
:search-attributes="['contractNumber','name']"
class="w-full"
:disabled="!itemInfo.customer"
>
<template #label>
{{ itemInfo.contract ? contracts.find(i => i.id === itemInfo.contract).name : "Kein Vertrag ausgewählt" }}
</template>
<template #option="{option: contract}">
{{ customers.find(i => i.id === contract.customer) ? customers.find(i => i.id === contract.customer).name : "" }}
- {{ contract.contractNumber }} - {{ contract.name }}
</template>
</USelectMenu>
<UButton
variant="outline"
color="rose"
v-if="itemInfo.contract"
icon="i-heroicons-x-mark"
@click="itemInfo.contract = null"
/>
<EntityModalButtons
type="projects"
:id="itemInfo.contract"
:create-query="{customer: itemInfo.customer}"
@return-data="(data) => itemInfo.contract = data.id"
/>
</InputGroup>
</UFormGroup>
</div>
</InputGroup>
<div v-if="itemInfo.type === 'serialInvoices'" class="mb-5">
<UDivider class="mt-5 mb-3">
Einstellungen für die Serienrechnung
</UDivider>
<div class="flex flex-row">
<div class="w-1/3">
<UFormGroup
label="Datum erste Ausführung:"
>
<UPopover :popper="{ placement: 'bottom-start' }">
<UButton
icon="i-heroicons-calendar-days-20-solid"
:label="itemInfo.serialConfig.firstExecution ? dayjs(itemInfo.serialConfig.firstExecution).format('DD.MM.YYYY') : 'Datum auswählen'"
variant="outline"
/>
<template #panel="{ close }">
<LazyDatePicker v-model="itemInfo.serialConfig.firstExecution" @close="close"/>
</template>
</UPopover>
</UFormGroup>
<UFormGroup
label="Datum letzte Ausführung:"
>
<UPopover :popper="{ placement: 'bottom-start' }">
<UButton
icon="i-heroicons-calendar-days-20-solid"
:label="itemInfo.serialConfig.executionUntil ? dayjs(itemInfo.serialConfig.executionUntil).format('DD.MM.YYYY') : 'Datum auswählen'"
variant="outline"
/>
<template #panel="{ close }">
<LazyDatePicker v-model="itemInfo.serialConfig.executionUntil" @close="close"/>
</template>
</UPopover>
</UFormGroup>
<UCheckbox
v-model="itemInfo.serialConfig.active"
label="Aktiv"
class="mt-3"
/>
</div>
<div class="w-2/3">
<UFormGroup
label="Intervall:"
>
<USelectMenu
v-model="itemInfo.serialConfig.intervall"
:options="['wöchentlich','2 - wöchentlich', 'monatlich', 'vierteljährlich','halbjährlich', 'jährlich']"
/>
</UFormGroup>
<UFormGroup
label="Richtung:"
>
<USelectMenu
v-model="itemInfo.serialConfig.dateDirection"
:options="['Rückwirkend','Im Voraus']"
/>
</UFormGroup>
<UAlert
title="Anfangs- und Enddatum"
description="Für das Anfangs- und Enddatum werden jeweils der ersten und letzte Tag des ausgewählten Intervalls und der Richtung automatisch ausgewählt"
class="mt-5"
/>
</div>
</div>
</div>
<UDivider
class="my-3"
/>
<UFormGroup
label="Titel:"
>
<UInput v-model="itemInfo.title" disabled/>
</UFormGroup>
<UFormGroup
label="Beschreibung:"
class="mt-3"
>
<UInput v-model="itemInfo.description"/>
</UFormGroup>
<UDivider
class="my-3"
/>
<UFormGroup
label="Vorlage auswählen"
>
<USelectMenu
:options="getTextTemplateByType(itemInfo.type,'startText')"
v-model="itemInfo.startText"
option-attribute="text"
value-attribute="text"
>
<template #option="{option}">
{{ option.name }} - {{ option.text }}
</template>
<template #label>
{{ texttemplates.find(i => i.text === itemInfo.startText && (itemInfo.type === "serialInvoices" ? i.documentType === "invoices" : i.documentType === itemInfo.type)) ? texttemplates.find(i => i.text === itemInfo.startText && (itemInfo.type === "serialInvoices" ? i.documentType === "invoices" : i.documentType === itemInfo.type)).name : "Keine Vorlage ausgewählt oder Vorlage verändert" }}
</template>
</USelectMenu>
</UFormGroup>
<UFormGroup
label="Einleitung:"
>
<UTextarea
v-model="itemInfo.startText"
:rows="6"
/>
</UFormGroup>
<UDivider
class="my-3"
/>
<table class="w-full" v-if="itemInfo.rows.length > 0">
<thead>
<tr>
<th></th>
<th>Pos.</th>
<th class="pl-2">Name</th>
<th class="pl-2">Menge</th>
<th class="pl-2">Einheit</th>
<th class="pl-2" v-if="itemInfo.type !== 'deliveryNotes'">Preis</th>
<!-- <th class="pl-2" v-if="itemInfo.type !== 'deliveryNotes'">Steuer</th>
<th class="pl-2" v-if="itemInfo.type !== 'deliveryNotes'">Rabatt</th>-->
<th class="pl-2"></th>
<th class="pl-2" v-if="itemInfo.type !== 'deliveryNotes'">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"
v-if="itemInfo.type !== 'cancellationInvoices'"
/>
</td>
<td
v-if="row.mode === 'pagebreak'"
colspan="7"
>
<UDivider/>
</td>
<td
v-if="row.mode === 'text'"
colspan="9"
>
<!-- <UInput
v-model="row.text"
placeholder="Titel"
maxlength="60"
/>-->
<UTextarea
v-model="row.description"
placeholder="Text"
/>
</td>
<td
v-if="!['pagebreak','text'].includes(row.mode)"
>{{ row.pos }}
</td>
<td
class="w-120"
v-if="row.mode === 'free'"
>
<UInput
:disabled="itemInfo.type === 'cancellationInvoices' || row.editDisabled"
v-model="row.text"
placeholder="Name"
class="min-w-40"
/>
</td>
<td
class="w-120"
v-else-if="row.mode === 'normal'"
>
<InputGroup class="w-full">
<USelectMenu
:disabled="itemInfo.type === 'cancellationInvoices'"
class="w-60"
:options="products"
:color="row.product ? 'primary' : 'rose'"
option-attribute="name"
value-attribute="id"
searchable
searchable-placeholder="Suche ..."
:search-attributes="['name']"
v-model="row.product"
@change="setRowData(row)"
>
<template #label>
<span class="truncate">{{
products.find(i => i.id === row.product) ? products.find(i => i.id === row.product).name : "Kein Produkt ausgewählt"
}}</span>
</template>
</USelectMenu>
<EntityModalButtons
type="products"
:id="row.product"
@return-data="(data) => setRowData(row,null,data)"
/>
<!-- <UButton
icon="i-heroicons-magnifying-glass"
@click="showProductSelectionModal = true"
/>
<UModal v-model="showProductSelectionModal">
<UCard>
<template #header>
Artikel Auswählen
</template>
<InputGroup class="w-full">
<UFormGroup label="Artikelkategorie:">
<USelectMenu
v-if="productcategories.length > 0"
:options="[{name: 'Nicht zugeordnet',id:'not set'},...productcategories]"
value-attribute="id"
option-attribute="name"
v-model="selectedProductcategorie"
/>
</UFormGroup>
</InputGroup>
<UTable
:rows="selectedProductcategorie !== 'not set' ? products.filter(i => i.productcategories.includes(selectedProductcategorie)) : products.filter(i => i.productcategories.length === 0)"
:columns="[
{key: 'name',label:'Name'},
{key: 'manufacturer',label:'Hersteller'},
{key: 'articleNumber',label:'Artikelnummer'},
]"
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Artikel anzuzeigen' }"
@select=" (i) => {
row.product = i.id
row.unit = products.find(i => i.id === row.product).unit,
row.price = (products.find(i => i.id === row.product).sellingPrice || 0),
row.description = products.find(i => i.id === row.product).description
showProductSelectionModal = false}"
>
</UTable>
</UCard>
</UModal>-->
</InputGroup>
</td>
<td
class="w-120"
v-else-if="row.mode === 'service'"
>
<InputGroup class="w-full">
<USelectMenu
:disabled="itemInfo.type === 'cancellationInvoices'"
class="w-60"
:options="services"
:color="row.service ? 'primary' : 'rose'"
option-attribute="name"
value-attribute="id"
searchable
searchable-placeholder="Suche ..."
:search-attributes="['name']"
v-model="row.service"
@change="setRowData(row)"
>
<template #label>
<span class="truncate">{{
services.find(i => i.id === row.service) ? services.find(i => i.id === row.service).name : "Keine Leistung ausgewählt"
}}</span>
</template>
</USelectMenu>
<EntityModalButtons
type="services"
:id="row.service"
@return-data="(data) => setRowData(row,data,null)"
/>
<!-- <UButton
icon="i-heroicons-magnifying-glass"
@click="showServiceSelectionModal = true"
/>
<UModal v-model="showServiceSelectionModal">
<UCard>
<template #header>
Leistung Auswählen
</template>
<InputGroup class="w-full">
<UFormGroup label="Leistungskategorie:">
<USelectMenu
v-if="servicecategories.length > 0"
:options="[{name: 'Nicht zugeordnet',id:'not set'},...servicecategories]"
value-attribute="id"
option-attribute="name"
v-model="selectedServicecategorie"
/>
</UFormGroup>
</InputGroup>
<UTable
:rows="selectedServicecategorie !== 'not set' ? services.filter(i => i.servicecategories.includes(selectedServicecategorie)) : services.filter(i => i.servicecategories.length === 0)"
:columns="[
{key: 'name',label:'Name'},
{key: 'serviceNumber',label:'Leistungsnummer'},
{key: 'sellingPrice',label:'Verkaufspreis'},
]"
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Leistungen anzuzeigen' }"
@select=" (i) => {
row.service = i.id
setRowData(row)
showServiceSelectionModal = false}"
>
</UTable>
</UCard>
</UModal>-->
</InputGroup>
</td>
<td
class="w-20"
v-if="!['pagebreak','title','text'].includes(row.mode)"
>
<UInput
v-model="row.quantity"
:disabled="itemInfo.type === 'cancellationInvoices'"
type="number"
:step="units.find(i => i.id === row.unit) ? units.find(i => i.id === row.unit).step : '1' "
/>
</td>
<td
class="w-40"
v-if="!['pagebreak','title','text'].includes(row.mode)"
>
<USelectMenu
v-model="row.unit"
:disabled="itemInfo.type === 'cancellationInvoices'"
:options="units"
option-attribute="name"
value-attribute="id"
>
<template #label>
{{ units.find(i => i.id === row.unit) ? units.find(i => i.id === row.unit).name : "Keine Einheit gewählt" }}
</template>
</USelectMenu>
</td>
<td
class="w-40"
v-if="!['pagebreak','title','text'].includes(row.mode) && itemInfo.type !== 'deliveryNotes'"
>
<UInput
v-model="row.inputPrice"
:disabled="itemInfo.type === 'cancellationInvoices'"
type="number"
step="0.001"
@change="updateCustomSurcharge"
>
<template #leading>
<span class="text-gray-500 dark:text-gray-400 text-xs">EUR</span>
</template>
<template #trailing>
<span
v-if="row.price > row.inputPrice"
class="text-primary text-xs">
{{ useCurrency(row.price) }}
</span>
<span
v-else-if="row.price < row.inputPrice"
class="text-rose-600 text-xs">
{{ useCurrency(row.price) }}
</span>
<span
v-else
class="text-gray-500 dark:text-gray-400 text-xs"> </span>
</template>
</UInput>
</td>
<!-- <td
class="w-40"
v-if="!['pagebreak','title','text'].includes(row.mode)&& itemInfo.type !== 'deliveryNotes'"
>
<USelectMenu
:options="[19,7,0]"
v-model="row.taxPercent"
:disabled="itemInfo.taxType === '13b UStG' || itemInfo.taxType === '19 UStG'"
>
<template #option="{option}">
{{option}} %
</template>
<template #label>
{{row.taxPercent}} %
</template>
</USelectMenu>
</td>-->
<!-- <td
class="w-40"
v-if="!['pagebreak','title','text'].includes(row.mode)&& itemInfo.type !== 'deliveryNotes'"
>
<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
class="w-40"
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"
@click="row.showEditDiesel = true"
/>
<UModal v-model="row.showEdit">
<UCard>
<!-- <template #header>
Zeile bearbeiten
</template>-->
<template #header>
<div class="flex items-center justify-between">
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
Zeile bearbeiten
</h3>
<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="units.find(i => i.id === row.unit) ? units.find(i => i.id === row.unit).step : '1' "
/>
</UFormGroup>
<UFormGroup
label="Einheit:"
class="flex-auto"
>
<USelectMenu
v-model="row.unit"
:options="units"
option-attribute="name"
value-attribute="id"
>
<template #label>
{{ units.find(i => i.id === row.unit) ? units.find(i => i.id === row.unit).name : "Keine Einheit gewählt" }}
</template>
</USelectMenu>
</UFormGroup>
</InputGroup>
<UFormGroup
label="Einzelpreis:"
v-if="itemInfo.type !== 'deliveryNotes'"
>
<UInput
v-model="row.inputPrice"
type="number"
step="0.001"
@change="updateCustomSurcharge"
>
<template #leading>
<span class="text-gray-500 dark:text-gray-400 text-xs">EUR</span>
</template>
<template #trailing>
<span
v-if="row.price > row.inputPrice"
class="text-primary text-xs">
{{ useCurrency(row.price) }}
</span>
<span
v-else-if="row.price < row.inputPrice"
class="text-rose-600 text-xs">
{{ useCurrency(row.price) }}
</span>
<span
v-else
class="text-gray-500 dark:text-gray-400 text-xs"> </span>
</template>
</UInput>
</UFormGroup>
<UFormGroup
label="Umsatzsteuer:"
class="mt-3"
v-if="itemInfo.type !== 'deliveryNotes'"
>
<USelectMenu
:options="[19,7,0]"
v-model="row.taxPercent"
:disabled="itemInfo.taxType === '13b UStG' || itemInfo.taxType === '19 UStG'"
>
<template #option="{option}">
{{ option }} %
</template>
<template #label>
{{ row.taxPercent }} %
</template>
</USelectMenu>
</UFormGroup>
<UFormGroup
label="Rabatt:"
class="mt-3"
v-if="itemInfo.type !== 'deliveryNotes'"
>
<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>
</UFormGroup>
<InputGroup class="w-full mt-3">
<UFormGroup
label="Optional:"
>
<UToggle
:disabled="row.alternative"
v-model="row.optional"
/>
</UFormGroup>
<UFormGroup
class="ml-3"
label="Alternativ:"
>
<UToggle
:disabled="row.optional"
v-model="row.alternative"
/>
</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"
>
<UTextarea
v-model="row.description"
rows="5"
>
</UTextarea>
</UFormGroup>
<!-- <template #footer>
<UButton
@click="row.showEdit = false"
>
Speichern
</UButton>
</template>-->
</UCard>
</UModal>
<UModal v-model="row.showEditDiesel">
<UCard>
<template #header>
Dieselverbrauch bearbeiten
</template>
<UFormGroup
label="Menge Diesel:"
>
<UInput
v-model="row.agriculture.dieselUsage"
@change="row.agriculture.adblueUsage = row.agriculture.dieselUsage * 0.04"
@input="row.agriculture.adblueUsage = row.agriculture.dieselUsage * 0.04"
type="number"
steps="0.01"
>
<template #trailing>
L
</template>
</UInput>
</UFormGroup>
<UFormGroup
label="Preis Diesel:"
>
<UInput
v-model="row.agriculture.dieselPrice"
@change="row.agriculture.adblueUsage = row.agriculture.dieselUsage * 0.04"
type="number"
steps="0.01"
>
<template #trailing>
€/L
</template>
</UInput>
</UFormGroup>
<UFormGroup
label="Menge AdBlue:"
>
<UInput
v-model="row.agriculture.adblueUsage"
type="number"
steps="0.01"
>
<template #trailing>
L
</template>
</UInput>
</UFormGroup>
<UFormGroup
label="Preis AdBlue:"
>
<UInput
v-model="row.agriculture.adbluePrice"
type="number"
steps="0.01"
>
<template #trailing>
€/L
</template>
</UInput>
</UFormGroup>
<template #footer>
<UButton
@click="row.showEditDiesel = false,
processDieselPosition()"
>
Speichern
</UButton>
</template>
</UCard>
</UModal>
</td>
<td
v-if="!['pagebreak','title','text'].includes(row.mode) && itemInfo.type !== 'deliveryNotes'"
>
<p class="text-right font-bold whitespace-nowrap">
{{ row.optional || row.alternative ? "(" : "" }}
<span v-if="row.discountPercent !== 0" class="line-through mr-2 text-rose-500">
{{ getRowAmountUndiscounted(row) }} €
</span>
{{ getRowAmount(row) }} €
{{ row.optional || row.alternative ? ")" : "" }}
</p>
</td>
<td
v-if="row.mode === 'title'"
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"
@click="removePosition(row.id)"
/>
</td>
</tr>
</template>
</draggable>
</table>
<UAlert
v-else
title="Keine Positionen hinzugefügt"
color="rose"
variant="outline"
icon="i-heroicons-light-bulb"
></UAlert>
<InputGroup>
<UButton
@click="addPosition('service')"
class="mt-3"
:disabled="['advanceInvoices','cancellationInvoices'].includes(itemInfo.type)"
>
+ Leistung
</UButton>
<UButton
@click="addPosition('normal')"
class="mt-3"
:disabled="['advanceInvoices','cancellationInvoices'].includes(itemInfo.type)"
>
+ Artikel
</UButton>
<UButton
@click="addPosition('free')"
class="mt-3"
: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
class="mt-5 mb-3"
v-if="openAdvanceInvoices.length > 0 || itemInfo.usedAdvanceInvoices.length > 0"
>
Noch nicht abgerechnete Abschlagsrechnungen
</UDivider>
<div
v-for="advanceInvoice in openAdvanceInvoices"
:key="advanceInvoice.id"
class="flex flex-row justify-between my-2"
>
<span>{{advanceInvoice.documentNumber}}</span>
<span>Summe: {{renderCurrency(advanceInvoice.rows[0].price * (1 + advanceInvoice.rows[0].taxPercent/100))}}</span>
<InputGroup>
<UButton
@click="addAdvanceInvoiceToInvoice(advanceInvoice.id)"
:disabled="itemInfo.usedAdvanceInvoices.includes(advanceInvoice.id)"
class="mr-2"
>
Verwenden
</UButton>
<UButton
@click="itemInfo.usedAdvanceInvoices = itemInfo.usedAdvanceInvoices.filter(i => i !== advanceInvoice.id)"
:disabled="!itemInfo.usedAdvanceInvoices.includes(advanceInvoice.id)"
color="rose"
variant="outline"
>
X
</UButton>
</InputGroup>
</div>-->
<UDivider class="my-3" v-if="Object.keys(documentTotal.titleSums).length > 0">Überschriften</UDivider>
<table>
<tr v-for="sumKey in Object.keys(documentTotal.titleSums) ">
<td>{{ sumKey }}</td>
<td>{{ useCurrency(documentTotal.titleSums[sumKey]) }}</td>
</tr>
</table>
<UDivider class="my-3" v-if="itemInfo.rows.length > 0">Auswertung & Gesamt</UDivider>
<div class="w-full flex justify-between" v-if="itemInfo.type !== 'deliveryNotes'">
<table class="w-1/2" v-if="itemInfo.rows.length > 0">
<tr v-if="documentReport.totalProductsPurchasePrice !== 0">
<td>Einkaufspreis Artikel Gesamt:</td>
<td class="text-right">{{ useCurrency(documentReport.totalProductsPurchasePrice) }}</td>
</tr>
<tr v-if="documentReport.totalProductsFromServicesPurchasePrice !== 0">
<td>Einkaufspreis Artikel aus Leistungen Gesamt:</td>
<td class="text-right">{{ useCurrency(documentReport.totalProductsFromServicesPurchasePrice) }}</td>
</tr>
<tr v-if="documentReport.totalHoursFromServices.totalPurchasePrice !== 0">
<td>Einkaufspreis Personal aus Leistungen Gesamt:</td>
<td class="text-right">{{ useCurrency(documentReport.totalHoursFromServices.totalPurchasePrice) }}</td>
</tr>
<tr>
<td>Gewinn Gesamt:</td>
<td class="text-right">{{ useCurrency(documentReport.totalMargin) }}</td>
</tr>
<tr v-if="documentReport.totalHoursSellingPrice !== 0">
<td>Lohnkosten Verkauf:</td>
<td class="text-right">{{ useCurrency(documentReport.totalHoursSellingPrice) }}</td>
</tr>
<tr v-for="key in Object.keys(documentReport.totalHoursFromServices.byName)">
<td>{{ key }}</td>
<td class="text-right">{{ documentReport.totalHoursFromServices.byName[key] }} h</td>
</tr>
<tr v-if="documentReport.totalHoursFromServices.total !== 0">
<td>Stunden Gesamt:</td>
<td class="text-right">{{ documentReport.totalHoursFromServices.total }} h</td>
</tr>
</table>
<table class="w-1/3 h-full" v-if="itemInfo.rows.length > 0">
<tr>
<td class="font-bold">Netto:</td>
<td class="text-right">{{ renderCurrency(documentTotal.totalNet) }}</td>
</tr>
<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>
</tr>
<tr v-if="documentTotal.totalGrossAlreadyPaid !== 0">
<td class="font-bold">Bereits bezahlt:</td>
<td class="text-right">{{ renderCurrency(documentTotal.totalGrossAlreadyPaid) }}</td>
</tr>
<tr v-if="documentTotal.totalGrossAlreadyPaid !== 0">
<td class="font-bold">Offene Summe:</td>
<td class="text-right">{{ renderCurrency(documentTotal.totalSumToPay) }}</td>
</tr>
</table>
</div>
<!-- <UDivider
class="my-3"
>Auswertung</UDivider>
<div class="w-full flex justify-end">
<table class="w-1/3">
<tr v-if="documentReport.totalProductsPurchasePrice !== 0">
<td>Einkaufspreis Artikel Gesamt:</td>
<td class="text-right">{{useCurrency(documentReport.totalProductsPurchasePrice)}}</td>
</tr>
<tr v-if="documentReport.totalProductsFromServicesPurchasePrice !== 0">
<td>Einkaufspreis Artikel aus Leistungen Gesamt:</td>
<td class="text-right">{{useCurrency(documentReport.totalProductsFromServicesPurchasePrice)}}</td>
</tr>
<tr v-if="documentReport.totalHoursFromServices.totalPurchasePrice !== 0">
<td>Einkaufspreis Personal aus Leistungen Gesamt:</td>
<td class="text-right">{{useCurrency(documentReport.totalHoursFromServices.totalPurchasePrice)}}</td>
</tr>
<tr>
<td>Gewinn Gesamt:</td>
<td class="text-right">{{useCurrency(documentReport.totalMargin)}}</td>
</tr>
<tr v-if="documentReport.totalHoursSellingPrice !== 0">
<td>Lohnkosten Verkauf:</td>
<td class="text-right">{{useCurrency(documentReport.totalHoursSellingPrice)}}</td>
</tr>
<tr v-for="key in Object.keys(documentReport.totalHoursFromServices.byName)">
<td>{{key}}</td>
<td class="text-right">{{documentReport.totalHoursFromServices.byName[key]}} h</td>
</tr>
<tr v-if="documentReport.totalHoursFromServices.total !== 0">
<td>Stunden Gesamt:</td>
<td class="text-right">{{documentReport.totalHoursFromServices.total}} h</td>
</tr>
</table>
</div>-->
<UDivider
class="my-3"
/>
<UFormGroup
label="Vorlage auswählen"
>
<USelectMenu
:options="getTextTemplateByType(itemInfo.type,'endText')"
v-model="itemInfo.endText"
option-attribute="text"
value-attribute="text"
>
<template #option="{option}">
{{option.name}} - {{option.text}}
</template>
<template #label>
{{texttemplates.find(i => i.text === itemInfo.endText && (itemInfo.type === "serialInvoices" ? i.documentType === "invoices" : i.documentType === itemInfo.type)) ? texttemplates.find(i => i.text === itemInfo.endText && (itemInfo.type === "serialInvoices" ? i.documentType === "invoices" : i.documentType === itemInfo.type)).name : "Keine Vorlage ausgewählt oder Vorlage verändert"}}
</template>
</USelectMenu>
</UFormGroup>
<UFormGroup
label="Nachbemerkung:"
>
<UTextarea
v-model="itemInfo.endText"
:rows="6"
/>
</UFormGroup>
</div>
<div v-else-if="item.label === 'Vorschau'">
<PDFViewer
v-if="showDocument"
:uri="uri"
location="edit_create_document"
/>
</div>
</template>
</UTabs>
<UProgress animation="carousel" v-else/>
</UDashboardPanelContent>
<PageLeaveGuard :when="guard"/>
</template>
<style scoped>
th {
text-align: left;
}
td {
padding: .4em;
}
/*tr:hover {
border: 1px solid #69c350;
}*/
.previewDocumentMobile {
aspect-ratio: 1 / 1.414;
}
.previewDocument {
height: 80vh;
}
</style>