feat(mcp): support serial invoice workflows
This commit is contained in:
@@ -10,12 +10,14 @@ import {
|
||||
folders,
|
||||
incominginvoices,
|
||||
ownaccounts,
|
||||
serialExecutions,
|
||||
statementallocations,
|
||||
vendors,
|
||||
} from "../../../db/schema"
|
||||
import { useNextNumberRangeNumber } from "../../utils/functions"
|
||||
import { saveFile } from "../../utils/files"
|
||||
import { insertHistoryItem } from "../../utils/history"
|
||||
import { executeManualGeneration, finishManualGeneration } from "../../modules/serialexecution.service"
|
||||
import {
|
||||
prepareStatementAllocationInput,
|
||||
statementAllocationUuidArg,
|
||||
@@ -43,6 +45,22 @@ const numberArg = (args: Record<string, unknown>, key: string) => {
|
||||
const hasValue = (value: unknown) => value !== null && value !== undefined && value !== ""
|
||||
const hasValidNumber = (value: unknown) => hasValue(value) && Number.isFinite(Number(value))
|
||||
const MAX_MCP_UPLOAD_BYTES = 20 * 1024 * 1024
|
||||
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
|
||||
|
||||
const serialConfigSchema = {
|
||||
type: "object",
|
||||
description: "Ausführungsplan einer Serienrechnungsvorlage.",
|
||||
properties: {
|
||||
firstExecution: { type: "string", description: "Datum der ersten Ausführung als ISO-8601-Wert." },
|
||||
executionUntil: { type: ["string", "null"], description: "Optionales Datum der letzten Ausführung als ISO-8601-Wert." },
|
||||
intervall: {
|
||||
type: "string",
|
||||
enum: ["wöchentlich", "2 - wöchentlich", "monatlich", "vierteljährlich", "halbjährlich", "jährlich"],
|
||||
},
|
||||
active: { type: "boolean" },
|
||||
dateDirection: { type: "string", enum: ["Rückwirkend", "Im Voraus"] },
|
||||
},
|
||||
}
|
||||
|
||||
const allowedOutgoingDocumentTypes = new Set([
|
||||
"quotes",
|
||||
@@ -501,7 +519,7 @@ export const accountingTools: McpTool[] = [
|
||||
type: "object",
|
||||
required: ["type"],
|
||||
properties: {
|
||||
type: { type: "string" },
|
||||
type: { type: "string", enum: [...allowedOutgoingDocumentTypes] },
|
||||
customer: { type: "number" },
|
||||
contact: { type: "number" },
|
||||
contract: { type: "number" },
|
||||
@@ -529,6 +547,7 @@ export const accountingTools: McpTool[] = [
|
||||
availableInPortal: { type: "boolean" },
|
||||
customSurchargePercentage: { type: "number" },
|
||||
report: { type: "object" },
|
||||
serialConfig: serialConfigSchema,
|
||||
},
|
||||
},
|
||||
async handler(context, args) {
|
||||
@@ -555,7 +574,7 @@ export const accountingTools: McpTool[] = [
|
||||
required: ["id"],
|
||||
properties: {
|
||||
id: { type: "number" },
|
||||
type: { type: "string" },
|
||||
type: { type: "string", enum: [...allowedOutgoingDocumentTypes] },
|
||||
state: { type: "string" },
|
||||
customer: { type: "number" },
|
||||
contact: { type: "number" },
|
||||
@@ -584,6 +603,7 @@ export const accountingTools: McpTool[] = [
|
||||
availableInPortal: { type: "boolean" },
|
||||
customSurchargePercentage: { type: "number" },
|
||||
report: { type: "object" },
|
||||
serialConfig: serialConfigSchema,
|
||||
},
|
||||
},
|
||||
async handler(context, args) {
|
||||
@@ -668,6 +688,90 @@ export const accountingTools: McpTool[] = [
|
||||
return { document: updated }
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "accounting.serial_invoices.execute",
|
||||
title: "Serienrechnungslauf starten",
|
||||
description: "Erzeugt aus ausgewählten aktiven Serienrechnungsvorlagen einen neuen Rechnungslauf.",
|
||||
requiredPermissions: ["accounting.outgoing_documents.write"],
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
required: ["executionDate", "templateIds"],
|
||||
properties: {
|
||||
executionDate: { type: "string", description: "Ausführungsdatum als ISO-8601-Wert." },
|
||||
templateIds: {
|
||||
type: "array",
|
||||
minItems: 1,
|
||||
uniqueItems: true,
|
||||
items: { type: "number" },
|
||||
},
|
||||
},
|
||||
},
|
||||
async handler(context, args) {
|
||||
const executionDate = stringArg(args, "executionDate")
|
||||
const parsedExecutionDate = executionDate ? new Date(executionDate) : null
|
||||
const templateIds = Array.isArray(args.templateIds)
|
||||
? [...new Set(args.templateIds.map(Number).filter((id) => Number.isFinite(id) && id > 0))]
|
||||
: []
|
||||
|
||||
if (!parsedExecutionDate || Number.isNaN(parsedExecutionDate.getTime())) {
|
||||
throw new Error("executionDate muss ein gültiger ISO-8601-Wert sein")
|
||||
}
|
||||
if (!templateIds.length) throw new Error("templateIds muss mindestens eine gültige ID enthalten")
|
||||
|
||||
return executeManualGeneration(
|
||||
context.server,
|
||||
parsedExecutionDate,
|
||||
templateIds,
|
||||
context.tenantId,
|
||||
context.userId,
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "accounting.serial_invoice_executions.list",
|
||||
title: "Serienrechnungsläufe auflisten",
|
||||
description: "Listet die zuletzt gestarteten Serienrechnungsläufe des aktiven Mandanten.",
|
||||
requiredPermissions: ["accounting.outgoing_documents.read"],
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
status: { type: "string", enum: ["draft", "completed", "error"] },
|
||||
limit: { type: "number", minimum: 1, maximum: 100 },
|
||||
},
|
||||
},
|
||||
async handler(context, args) {
|
||||
const conditions = [eq(serialExecutions.tenant, context.tenantId)]
|
||||
const status = stringArg(args, "status")
|
||||
if (status) conditions.push(eq(serialExecutions.status, status))
|
||||
|
||||
const rows = await context.server.db
|
||||
.select()
|
||||
.from(serialExecutions)
|
||||
.where(and(...conditions))
|
||||
.orderBy(desc(serialExecutions.createdAt))
|
||||
.limit(limitFromArgs(args))
|
||||
|
||||
return { rows }
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "accounting.serial_invoice_executions.finish",
|
||||
title: "Serienrechnungslauf abschließen",
|
||||
description: "Finalisiert die erzeugten Rechnungen eines Serienrechnungslaufs und schließt den Lauf ab.",
|
||||
requiredPermissions: ["accounting.outgoing_documents.write"],
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
required: ["id"],
|
||||
properties: {
|
||||
id: { type: "string", description: "UUID des Serienrechnungslaufs." },
|
||||
},
|
||||
},
|
||||
async handler(context, args) {
|
||||
const id = stringArg(args, "id")
|
||||
if (!id || !UUID_PATTERN.test(id)) throw new Error("id muss eine gültige UUID sein")
|
||||
return finishManualGeneration(context.server, id, context.tenantId)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "accounting.outgoing_documents.archive",
|
||||
title: "Ausgangsbeleg archivieren",
|
||||
|
||||
Reference in New Issue
Block a user