KI-AGENT: MCP-Belegpositionen für Angebote normalisieren

This commit is contained in:
2026-05-15 17:11:31 +02:00
parent cb939f2197
commit 683d073b6e
2 changed files with 85 additions and 9 deletions

View File

@@ -1,4 +1,5 @@
import { and, desc, eq, ilike, or } from "drizzle-orm" import { and, desc, eq, ilike, or } from "drizzle-orm"
import { randomUUID } from "node:crypto"
import { import {
accounts, accounts,
bankstatements, bankstatements,
@@ -102,7 +103,7 @@ const applyOutgoingDocumentTaxType = (
const rows = Array.isArray(payload.rows) const rows = Array.isArray(payload.rows)
? payload.rows ? payload.rows
: Array.isArray(existingRows) : Array.isArray(existingRows)
? existingRows ? normalizeOutgoingDocumentRows(existingRows)
: null : null
if (!rows) return if (!rows) return
@@ -124,6 +125,44 @@ const optionalArrayArg = (args: Record<string, unknown>, key: string) => {
return Array.isArray(value) ? value : null return Array.isArray(value) ? value : null
} }
const normalizeOutgoingDocumentRow = (row: unknown, index: number) => {
let normalizedRow = row
if (typeof normalizedRow === "string") {
try {
normalizedRow = JSON.parse(normalizedRow)
} catch {
throw new Error(`Position ${index + 1} ist kein gültiges JSON-Objekt`)
}
}
if (!normalizedRow || typeof normalizedRow !== "object" || Array.isArray(normalizedRow)) {
throw new Error(`Position ${index + 1} muss ein Objekt sein`)
}
const rowPayload = { ...(normalizedRow as Record<string, any>) }
rowPayload.id = rowPayload.id || randomUUID()
rowPayload.pos = rowPayload.pos || String(index + 1)
rowPayload.mode = rowPayload.mode || "free"
rowPayload.inputPrice = hasValidNumber(rowPayload.inputPrice)
? Number(rowPayload.inputPrice)
: hasValidNumber(rowPayload.price)
? Number(rowPayload.price)
: 0
rowPayload.price = hasValidNumber(rowPayload.price) ? Number(rowPayload.price) : rowPayload.inputPrice
rowPayload.quantity = hasValidNumber(rowPayload.quantity) ? Number(rowPayload.quantity) : 1
rowPayload.discountPercent = hasValidNumber(rowPayload.discountPercent) ? Number(rowPayload.discountPercent) : 0
rowPayload.linkedEntitys = Array.isArray(rowPayload.linkedEntitys) ? rowPayload.linkedEntitys : []
return rowPayload
}
const normalizeOutgoingDocumentRows = (rows: unknown) => {
if (!Array.isArray(rows)) return []
return rows.map((row, index) => normalizeOutgoingDocumentRow(row, index))
}
const buildOutgoingDocumentPayload = ( const buildOutgoingDocumentPayload = (
args: Record<string, unknown>, args: Record<string, unknown>,
userId: string, userId: string,
@@ -142,7 +181,7 @@ const buildOutgoingDocumentPayload = (
payload.archived = false payload.archived = false
payload.state = stringArg(args, "state") || "Entwurf" payload.state = stringArg(args, "state") || "Entwurf"
payload.type = documentTypeArg(args) payload.type = documentTypeArg(args)
payload.rows = optionalArrayArg(args, "rows") || [] payload.rows = normalizeOutgoingDocumentRows(optionalArrayArg(args, "rows") || [])
} }
const stringFields = [ const stringFields = [
@@ -176,7 +215,7 @@ const buildOutgoingDocumentPayload = (
if (args.agriculture !== undefined) payload.agriculture = optionalObjectArg(args, "agriculture") if (args.agriculture !== undefined) payload.agriculture = optionalObjectArg(args, "agriculture")
if (args.report !== undefined) payload.report = optionalObjectArg(args, "report") || {} if (args.report !== undefined) payload.report = optionalObjectArg(args, "report") || {}
if (args.serialConfig !== undefined) payload.serialConfig = optionalObjectArg(args, "serialConfig") || {} if (args.serialConfig !== undefined) payload.serialConfig = optionalObjectArg(args, "serialConfig") || {}
if (args.rows !== undefined) payload.rows = optionalArrayArg(args, "rows") || [] if (args.rows !== undefined) payload.rows = normalizeOutgoingDocumentRows(optionalArrayArg(args, "rows") || [])
if (args.usedAdvanceInvoices !== undefined) payload.usedAdvanceInvoices = optionalArrayArg(args, "usedAdvanceInvoices") || [] if (args.usedAdvanceInvoices !== undefined) payload.usedAdvanceInvoices = optionalArrayArg(args, "usedAdvanceInvoices") || []
if (typeof args.availableInPortal === "boolean") payload.availableInPortal = args.availableInPortal if (typeof args.availableInPortal === "boolean") payload.availableInPortal = args.availableInPortal
if (typeof args.advanceInvoiceResolved === "boolean") payload.advanceInvoiceResolved = args.advanceInvoiceResolved if (typeof args.advanceInvoiceResolved === "boolean") payload.advanceInvoiceResolved = args.advanceInvoiceResolved

View File

@@ -216,6 +216,38 @@ const normalizeEntityId = (value) => {
if (value === null || typeof value === "undefined") return null if (value === null || typeof value === "undefined") return null
return typeof value === "object" ? (value.id ?? null) : value return typeof value === "object" ? (value.id ?? null) : value
} }
const normalizeCreatedDocumentRow = (row) => {
let normalizedRow = row
if (typeof normalizedRow === "string") {
try {
normalizedRow = JSON.parse(normalizedRow)
} catch {
normalizedRow = {
id: uuidv4(),
mode: "text",
text: normalizedRow,
}
}
}
if (!normalizedRow || typeof normalizedRow !== "object" || Array.isArray(normalizedRow)) {
normalizedRow = {
id: uuidv4(),
mode: "text",
text: String(row ?? ""),
}
}
return {
...normalizedRow,
id: normalizedRow.id || uuidv4(),
linkedEntitys: Array.isArray(normalizedRow.linkedEntitys) ? normalizedRow.linkedEntitys : [],
}
}
const normalizeCreatedDocumentRows = (rows) => Array.isArray(rows)
? rows.map((row) => normalizeCreatedDocumentRow(row))
: []
const setupPage = async () => { const setupPage = async () => {
await setupData() await setupData()
@@ -227,6 +259,7 @@ const setupPage = async () => {
if (route.params.id) { if (route.params.id) {
console.log(route.params) console.log(route.params)
itemInfo.value = await useEntities("createddocuments").selectSingle(route.params.id,'',false) itemInfo.value = await useEntities("createddocuments").selectSingle(route.params.id,'',false)
itemInfo.value.rows = normalizeCreatedDocumentRows(itemInfo.value.rows)
itemInfo.value.taxType = normalizeTaxTypeValue(itemInfo.value.taxType) itemInfo.value.taxType = normalizeTaxTypeValue(itemInfo.value.taxType)
await setContactPersonData() await setContactPersonData()
checkCompatibilityWithInputPrice() checkCompatibilityWithInputPrice()
@@ -235,6 +268,8 @@ const setupPage = async () => {
if (!itemInfo.value.deliveryDateType) itemInfo.value.deliveryDateType = "Lieferdatum" if (!itemInfo.value.deliveryDateType) itemInfo.value.deliveryDateType = "Lieferdatum"
itemInfo.value.rows = normalizeCreatedDocumentRows(itemInfo.value.rows)
if (itemInfo.value.rows.find(i => i.agriculture)) { if (itemInfo.value.rows.find(i => i.agriculture)) {
processDieselPosition() processDieselPosition()
} }
@@ -284,7 +319,7 @@ const setupPage = async () => {
mode: "title", mode: "title",
text: `${doc.title} vom ${dayjs(doc.documentDate).format("DD.MM.YYYY")}` text: `${doc.title} vom ${dayjs(doc.documentDate).format("DD.MM.YYYY")}`
}, },
...doc.rows ...normalizeCreatedDocumentRows(doc.rows)
]) ])
}) })
@@ -305,7 +340,7 @@ const setupPage = async () => {
console.log(linkedDocuments) console.log(linkedDocuments)
if (linkedDocuments.find(i => i.rows.find(x => x.agriculture.dieselUsage))) { if (linkedDocuments.find(i => normalizeCreatedDocumentRows(i.rows).find(x => x.agriculture?.dieselUsage))) {
console.log("has diesel") console.log("has diesel")
//Remove Existing Total Diesel Pos //Remove Existing Total Diesel Pos
@@ -347,7 +382,7 @@ const setupPage = async () => {
text: linkedDocument.title, text: linkedDocument.title,
}) })
itemInfo.value.rows.push(...linkedDocument.rows) itemInfo.value.rows.push(...normalizeCreatedDocumentRows(linkedDocument.rows))
} }
for await (const doc of linkedDocuments.filter(i => quoteLikeDocumentTypes.includes(i.type))) { for await (const doc of linkedDocuments.filter(i => quoteLikeDocumentTypes.includes(i.type))) {
@@ -359,7 +394,7 @@ const setupPage = async () => {
text: linkedDocument.title, text: linkedDocument.title,
}) })
itemInfo.value.rows.push(...linkedDocument.rows) itemInfo.value.rows.push(...normalizeCreatedDocumentRows(linkedDocument.rows))
} }
itemInfo.value.rows.push({ itemInfo.value.rows.push({
@@ -428,7 +463,7 @@ const setupPage = async () => {
if (optionsToImport.title) itemInfo.value.title = linkedDocument.title if (optionsToImport.title) itemInfo.value.title = linkedDocument.title
if (optionsToImport.description) itemInfo.value.description = linkedDocument.description if (optionsToImport.description) itemInfo.value.description = linkedDocument.description
if (optionsToImport.startText) itemInfo.value.startText = linkedDocument.startText if (optionsToImport.startText) itemInfo.value.startText = linkedDocument.startText
if (optionsToImport.rows) itemInfo.value.rows = linkedDocument.rows if (optionsToImport.rows) itemInfo.value.rows = normalizeCreatedDocumentRows(linkedDocument.rows)
if (optionsToImport.endText) itemInfo.value.endText = linkedDocument.endText if (optionsToImport.endText) itemInfo.value.endText = linkedDocument.endText
} else { } else {
@@ -454,7 +489,7 @@ const setupPage = async () => {
itemInfo.value.title = linkedDocument.title itemInfo.value.title = linkedDocument.title
itemInfo.value.description = linkedDocument.description itemInfo.value.description = linkedDocument.description
itemInfo.value.startText = linkedDocument.startText itemInfo.value.startText = linkedDocument.startText
itemInfo.value.rows = linkedDocument.rows itemInfo.value.rows = normalizeCreatedDocumentRows(linkedDocument.rows)
itemInfo.value.endText = linkedDocument.endText itemInfo.value.endText = linkedDocument.endText
} }
@@ -1628,6 +1663,8 @@ const getTextTemplateByType = (type, pos) => {
} }
const checkCompatibilityWithInputPrice = () => { const checkCompatibilityWithInputPrice = () => {
itemInfo.value.rows = normalizeCreatedDocumentRows(itemInfo.value.rows)
itemInfo.value.rows.forEach(row => { itemInfo.value.rows.forEach(row => {
if (!row.inputPrice) { if (!row.inputPrice) {
row.inputPrice = row.price row.inputPrice = row.price