Files
FEDEO/pages/createDocument/edit/[[id]].vue

2757 lines
102 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 supabase = useSupabaseClient()
const modal = useModal()
definePageMeta({
middleware: "auth"
})
const showProductSelectionModal = ref(false)
const showServiceSelectionModal = ref(false)
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: profileStore.ownTenant.standardPaymentDays,
customSurchargePercentage: 0,
createdBy: profileStore.activeProfile.id,
title: null,
description: null,
startText: null,
endText: null,
rows: [
],
contactPerson: profileStore.activeProfile.id,
contactPersonName: null,
contactTel: null,
contactEMail: null,
serialConfig: {
firstExecution: "",
intervall: "monatlich",
executionUntil: "",
active: true,
dateDirection: "Rückwirkend",
},
letterhead: null,
agriculture: {},
usedAdvanceInvoices: []
})
console.log(profileStore.ownTenant)
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 texttemplates = ref([])
const loaded = ref(false)
const setupPage = async () => {
letterheads.value = (await useSupabaseSelect("letterheads","*")).filter(i => i.documentTypes.length === 0 || i.documentTypes.includes(itemInfo.value.type))
createddocuments.value = (await useSupabaseSelect("createddocuments","*"))
projects.value = (await useSupabaseSelect("projects","*"))
plants.value = (await useSupabaseSelect("plants","*"))
services.value = (await useSupabaseSelect("services","*"))
servicecategories.value = (await useSupabaseSelect("servicecategories","*"))
products.value = (await useSupabaseSelect("products","*"))
productcategories.value = (await useSupabaseSelect("productcategories","*"))
customers.value = (await useSupabaseSelect("customers","*","customerNumber"))
contacts.value = (await useSupabaseSelect("contacts","*"))
texttemplates.value = (await useSupabaseSelect("texttemplates","*"))
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) {
itemInfo.value = await useSupabaseSelectSingle("createddocuments", route.params.id)
checkCompatibilityWithInputPrice()
}
if(itemInfo.value.project) checkForOpenAdvanceInvoices()
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)
}
setContactPersonData()
if(route.query.linkedDocuments) {
let linkedDocuments = (await supabase.from("createddocuments").select().in("id",JSON.parse(route.query.linkedDocuments))).data
//TODO: Implement Checking for Same Customer, Contact and Project
itemInfo.value.customer = linkedDocuments[0].customer
itemInfo.value.project = linkedDocuments[0].project
itemInfo.value.contact = linkedDocuments[0].contact
setCustomerData()
let firstDate = null
let lastDate = null
linkedDocuments.forEach(doc => {
let lastId = 0
itemInfo.value.rows.forEach(row => {
if(row.id > lastId) lastId = row.id
})
if(dayjs(doc.documentDate).isBefore(firstDate) || !firstDate) firstDate = doc.documentDate
if(dayjs(doc.documentDate).isAfter(lastDate) || !lastDate) lastDate = doc.documentDate
itemInfo.value.rows.push(...[
{
id:uuidv4(),
mode: "title",
text: `${doc.title} vom ${dayjs(doc.documentDate).format("DD.MM.YYYY")}`
},
...doc.rows
])
})
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()
}
}
if(route.query.linkedDocument) {
itemInfo.value.linkedDocument = route.query.linkedDocument
let linkedDocument = await useSupabaseSelectSingle("createddocuments",itemInfo.value.linkedDocument)
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.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
itemInfo.value.taxType = linkedDocument.taxType
itemInfo.value.customer = linkedDocument.customer
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.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
}
setCustomerData(null,true)
checkCompatibilityWithInputPrice()
if(route.query.loadMode === "storno") {
itemInfo.value.rows.forEach(row => {
row.price = row.price * -1
})
itemInfo.value.description = `Stornorechnung zu Rechnung ${linkedDocument.documentNumber} vom ${dayjs(linkedDocument.documentDate).format('DD.MM.YYYY')}`
itemInfo.value.type = "cancellationInvoices"
setDocumentTypeConfig(true)
}
}
if(route.query.project) {
itemInfo.value.project = Number(route.query.project)
let project = await useSupabaseSelectSingle("projects",itemInfo.value.project)
if(!itemInfo.value.description){
itemInfo.value.description = project.customerRef
}
checkForOpenAdvanceInvoices()
}
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()
}
}
//if(itemInfo.value.project) checkForOpenAdvanceInvoices()
loaded.value = true
}
setupPage()
const openAdvanceInvoices = ref([])
const checkForOpenAdvanceInvoices = async () => {
console.log("Check for Open Advance Invoices")
const {data,error} = await supabase.from("createddocuments").select().eq("project", itemInfo.value.project).eq("advanceInvoiceResolved", false).eq("type","advanceInvoices")
console.log(data)
openAdvanceInvoices.value = data
}
const addAdvanceInvoiceToInvoice = (advanceInvoice) => {
itemInfo.value.usedAdvanceInvoices.push(advanceInvoice)
}
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 === "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 useSupabaseSelect("customers")
let customer = customers.value.find(i => i.id === itemInfo.value.customer)
console.log(customer)
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.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)
let profile = await useSupabaseSelectSingle("profiles",itemInfo.value.contactPerson, '*')
itemInfo.value.contactPersonName = profile.fullName
itemInfo.value.contactTel = profile.mobileTel || profile.fixedTel || ""
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
}
itemInfo.value.rows.push({...rowData, ...profileStore.ownTenant.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
})
} else if(mode === 'service'){
let rowData = {
id: uuidv4(),
mode: "service",
quantity: 1,
inputPrice: 0,
price: 0,
taxPercent: taxPercentage,
discountPercent: 0,
unit: 1
}
//Push Agriculture Holder only if Module is activated
itemInfo.value.rows.push({...rowData, ...profileStore.ownTenant.extraModules.includes("agriculture") ? {agriculture: {}}: {}})
} else if(mode === "pagebreak") {
itemInfo.value.rows.push({
id: uuidv4(),
mode: "pagebreak",
})
} else if(mode === "title") {
itemInfo.value.rows.push({
id: uuidv4(),
mode: "title",
})
} else if(mode === "text") {
itemInfo.value.rows.push({
id: uuidv4(),
mode: "text",
})
}
setPosNumbers()
}
const removePosition = (id) => {
itemInfo.value.rows = itemInfo.value.rows.filter(row => row.id !== id)
setPosNumbers()
}
const getRowMargin = (row) => {
if(row.mode === "normal" && row.product) {
let purchasePrice = products.value.find(i => i.id === row.product).purchasePrice || 0
return row.price - purchasePrice
} else {
return 0
}
}
const findDocumentErrors = computed(() => {
let errors = []
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.address.street === null) errors.push({message: "Es ist keine Straße im Adressat angegeben", type: "breaking"})
if(itemInfo.value.address.zip === null) errors.push({message: "Es ist keine Postleitzahl im Adressat angegeben", type: "breaking"})
if(itemInfo.value.address.city === null) 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(['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 => {
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") errors.push({message: `In Position ${row.pos} ist kein Steuersatz hinterlegt`, type: "breaking"})
if(!row.price && typeof row.price !== "number") 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(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,b) => (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(".",",") + " €"
}
const documentTotal = computed(() => {
let totalNet = 0
let total19 = 0
let total7 = 0
itemInfo.value.rows.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)
} else if(row.taxPercent === 7) {
total7 = total7 + Number(rowPrice * 0.07)
}
}
})
//Title Sum
let titleSums = {}
let lastTitle = ""
itemInfo.value.rows.forEach(row => {
if(row.mode === 'title'){
titleSums[`${row.pos} - ${row.text}`] = 0
lastTitle = `${row.pos} - ${row.text}`
} else if(!['pagebreak','text'].includes(row.mode) && lastTitle !== ""){
titleSums[lastTitle] = Number(titleSums[lastTitle]) + Number(Number(row.quantity) * Number(row.price) * (1 - Number(row.discountPercent) /100) )
}
})
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 = totalGross - totalGrossAlreadyPaid
return {
titleSums: titleSums,
totalNet: totalNet,
total19: total19,
total7: total7,
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.forEach(row => {
if(row.product) {
let product = products.value.find(i => i.id === row.product)
console.log(product)
totalProductsPurchasePrice += product.purchasePrice * row.quantity
} else if(row.service) {
let service = services.value.find(i => i.id === row.service)
console.log(service)
if(service.materialComposition) {
service.materialComposition.forEach(entry => {
let productData = products.value.find(i => i.id === entry.product)
console.log(productData)
totalProductsFromServicesPurchasePrice += productData.purchasePrice * entry.quantity * row.quantity
})
}
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 = () => {
let customerData = customers.value.find(i => i.id === itemInfo.value.customer)
let contactData = dataStore.getContactById(itemInfo.value.contact)
let businessInfo = profileStore.ownTenant.businessInfo
if(profileStore.ownTenant.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 = dataStore.units.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,
diesel_gesamtverbrauch: (itemInfo.agriculture && itemInfo.agriculture.dieselUsageTotal) && itemInfo.agriculture.dieselUsageTotal
}
}
let contactPerson = profileStore.getProfileById(itemInfo.value.contactPerson)
let returnTitleSums = {}
if(Object.keys(documentTotal.value.titleSums).length > 0) {
Object.keys(documentTotal.value.titleSums).forEach(key => {
returnTitleSums[key] = renderCurrency(documentTotal.value.titleSums[key])
})
}
console.log(returnTitleSums)
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.fullName,
},
... contactPerson.fixedTel || contactPerson.mobileTel ? [{
label: "Telefon",
content: contactPerson.fixedTel || contactPerson.mobileTel,
}] : [],
... 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
}]: []
],
title: itemInfo.value.title,
description: itemInfo.value.description,
endText: templateEndText(generateContext(itemInfo.value, contactData)),
startText: templateStartText(generateContext(itemInfo.value, contactData)),
rows: rows,
total: {
totalNet: renderCurrency(documentTotal.value.totalNet),
total19: renderCurrency(documentTotal.value.total19),
totalGross: renderCurrency(documentTotal.value.totalGross),
totalGrossAlreadyPaid: renderCurrency(documentTotal.value.totalGrossAlreadyPaid),
totalSumToPay: renderCurrency(documentTotal.value.totalSumToPay),
titleSums: returnTitleSums
},
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 () => {
const ownTenant = profileStore.ownTenant
const path = letterheads.value.find(i => i.id === itemInfo.value.letterhead).path
/*const {data,error} = await supabase.functions.invoke('create_pdf',{
body: {
invoiceData: getDocumentData(),
backgroundPath: path,
returnMode: "base64"
}
})*/
uri.value = await useFunctions().useCreatePDF(getDocumentData(), path)
//const {data,error} = await supabase.storage.from("files").download(path)
//console.log(data)
//console.log(error)
//console.log(JSON.stringify(getDocumentData()))
//uri.value = `data:${data.mimeType};base64,${data.base64}`
//uri.value = await useCreatePdf(getDocumentData(), await data.arrayBuffer())
//alert(uri.value)
showDocument.value = true
//console.log(uri.value)
}
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,
address: itemInfo.value.address,
project: itemInfo.value.project,
paymentDays: itemInfo.value.paymentDays,
deliveryDateType: itemInfo.value.deliveryDateType,
createdBy: itemInfo.value.createdBy,
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,
}
let data = null
if(route.params.id) {
data = await dataStore.updateItem("createddocuments", {...createData, id: itemInfo.value.id})
} else {
data = await dataStore.createNewItem("createddocuments", 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(profileStore.ownTenant.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: (itemInfo.value.type === "invoices" || itemInfo.value.type === "quotes") ? itemInfo.value.taxType : null,
state: itemInfo.value.state || "Entwurf",
customer: itemInfo.value.customer,
contact: itemInfo.value.contact,
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,
deliveryDateType: itemInfo.value.deliveryDateType,
info: {},
createdBy: itemInfo.value.createdBy,
title: itemInfo.value.title,
description: itemInfo.value.description,
startText: itemInfo.value.startText,
endText: itemInfo.value.endText,
rows: itemInfo.value.rows,
contactPerson: itemInfo.value.contactPerson,
linkedDocument: itemInfo.value.linkedDocument,
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})
} else {
const data = await dataStore.createNewItem("createddocuments", createData)
console.log(data)
await router.push(`/createDocument/edit/${data.id}`)
}
if(resetup) await setupPage()
}
const closeDocument = async () => {
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"
}
fileData.folder = (await supabase.from("folders").select("id").eq("tenant", profileStore.currentTenant).eq("function", mappedType).eq("year",dayjs().format("YYYY")).single()).data.id
let tag = (await supabase.from("filetags").select("id").eq("tenant", profileStore.currentTenant).eq("createddocumenttype", mappedType).single()).data
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 dataStore.uploadFiles(fileData, [file],[tag.id], true)
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 => {
console.log(row)
if(!row.inputPrice) {
row.inputPrice = row.price
}
})
}
const updateCustomSurcharge = () => {
itemInfo.value.rows.forEach(row => {
if(!["pagebreak","title","text"].includes(row.mode)) {
//setRowData(row)
row.price = Number((row.inputPrice * (1 + itemInfo.value.customSurchargePercentage /100)).toFixed(2))
}
})
}
const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {}) => {
console.log("Set Row Data")
if(service && service.id) {
row.service = service.id
services.value = await useSupabaseSelect("services","*")
}
if(product && product.id) {
row.product = product.id
product.value = await useSupabaseSelect("products","*")
}
if(row.service) {
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.sellingPrice ? product.sellingPrice : products.value.find(i => i.id === row.product).sellingPrice)
//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.taxPercentage ? product.taxPercentage : products.value.find(i => i.id === row.product).taxPercentage
}
}
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>
<ButtonWithConfirm
v-if="itemInfo.state === 'Entwurf'"
color="rose"
variant="outline"
@confirmed="dataStore.updateItem('createddocuments',{id:itemInfo.id,archived: true}),
router.push('/createDocument')"
>
<template #button>
Archivieren
</template>
<template #header>
<span class="text-md dark:text-whitetext-black font-bold">Archivieren bestätigen</span>
</template>
Möchten Sie diesen Ausgangsbeleg wirklich archivieren?
</ButtonWithConfirm>
<UButton
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'"
>
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">
<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','quotes','confirmationOrders'].includes(itemInfo.type)"
>
<USelectMenu
:options="[{key:'Standard', label: 'Standard'},{key:'13b UStG', label: '13b UStG'},{key:'19 UStG', label: '19 UStG Kleinunternehmer'},{key:'12.3 UStG', label: 'PV 0% USt'}]"
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 ? dataStore.getCustomerById(itemInfo.customer).isCompany : false "
>
<InputGroup>
<USelectMenu
:options="contacts.filter(i => i.customer === 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">{{dataStore.getContactById(itemInfo.contact) ? dataStore.getContactById(itemInfo.contact).fullName : "Kein Kontakt ausgewählt"}}</span>
<UIcon name="i-heroicons-chevron-right-20-solid" class="w-5 h-5 transition-transform text-gray-400 dark:text-gray-500" :class="['transform rotate-90']" />
</UButton>
<template #label>
{{dataStore.getContactById(itemInfo.contact) ? dataStore.getContactById(itemInfo.contact).fullName : "Kein Kontakt ausgewählt"}}
</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>-->
<EntityModalButtons
type="contacts"
:id="itemInfo.contact"
:create-query="{customerId: itemInfo.customer}"
@return-data="(data) => itemInfo.contact = data.id"
/>
</InputGroup>
</UFormGroup>
<UFormGroup
label="Adresse:"
>
<UInput
v-model="itemInfo.address.street"
:placeholder="dataStore.getCustomerById(itemInfo.customer) ? dataStore.getCustomerById(itemInfo.customer).infoData.street : 'Straße + Hausnummer'"
:color="itemInfo.address.street ? 'primary' : 'rose'"
/>
<UInput
v-model="itemInfo.address.special"
class="mt-3"
:placeholder="dataStore.getCustomerById(itemInfo.customer) ? dataStore.getCustomerById(itemInfo.customer).infoData.special : 'Adresszusatz'"
/>
<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'"
:color="itemInfo.address.zip ? 'primary' : 'rose'"
/>
<UInput
class="flex-auto"
v-model="itemInfo.address.city"
:placeholder="dataStore.getCustomerById(itemInfo.customer) ? dataStore.getCustomerById(itemInfo.customer).infoData.city : 'Ort'"
: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="Individueller Aufschlag:"
>
<UInput
type="number"
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="Ansprechpartner:"
>
<USelectMenu
:options="profileStore.profiles"
v-model="itemInfo.contactPerson"
option-attribute="fullName"
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 === 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"
@change="checkForOpenAdvanceInvoices"
>
<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 === 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"
@change="checkForOpenAdvanceInvoices"
>
<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>
<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>
</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>
{{dataStore.texttemplates.find(i => i.text === itemInfo.startText && (itemInfo.type === "serialInvoices" ? i.documentType === "invoices" : i.documentType === itemInfo.type)) ? dataStore.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"
/>
</td>
<td
v-if="row.mode === 'pagebreak'"
colspan="9"
>
<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
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
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
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"
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="!['pagebreak','title','text'].includes(row.mode)"
>
<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
class="w-40"
v-if="!['pagebreak','title','text'].includes(row.mode) && 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>
</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
icon="i-heroicons-document-text"
@click="row.showEdit = true"
/>
<UButton
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>
<UFormGroup
label="Umsatzsteuer:"
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:"
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>
<UFormGroup
label="Beschreibung:"
>
<UTextarea
v-model="row.description"
>
</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"><span v-if="row.discountPercent !== 0" class="line-through mr-2 text-rose-500">{{getRowAmountUndiscounted(row)}} €</span>{{getRowAmount(row)}} €</p>
</td>
<td
v-if="row.mode === 'title'"
colspan="6"
>
<UInput
v-model="row.text"
placeholder="Titel"
/>
</td>
<td>
<UButton
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="itemInfo.type === 'advanceInvoices'"
>
+ Leistung
</UButton>
<UButton
@click="addPosition('normal')"
class="mt-3"
:disabled="itemInfo.type === 'advanceInvoices'"
>
+ Artikel
</UButton>
<UButton
@click="addPosition('free')"
class="mt-3"
:disabled="itemInfo.type === 'advanceInvoices'"
>
+ Freie Position
</UButton>
<UButton
@click="addPosition('pagebreak')"
class="mt-3"
>
+ Seitenumbruch
</UButton>
<UButton
@click="addPosition('title')"
class="mt-3"
>
+ Titel
</UButton>
<UButton
@click="addPosition('text')"
class="mt-3"
>
+ Text
</UButton>
</InputGroup>
<UDivider
class="mt-5 mb-3"
v-if="openAdvanceInvoices.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'">
<td class="font-bold">zzgl. 19 % USt:</td>
<td class="text-right">{{renderCurrency(documentTotal.total19)}}</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>
{{dataStore.texttemplates.find(i => i.text === itemInfo.endText && (itemInfo.type === "serialInvoices" ? i.documentType === "invoices" : i.documentType === itemInfo.type)) ? dataStore.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'">
<!-- <UButton
@click="generateDocument"
>
Show
</UButton>-->
<object
:data="uri"
v-if="showDocument"
type="application/pdf"
class="w-full previewDocumentMobile"
/>
</div>
</template>
</UTabs>
<UProgress animation="carousel" v-else/>
</UDashboardPanelContent>
</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>