3228 lines
125 KiB
Vue
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>
|