MCP-Bankzuweisungen ergänzen
All checks were successful
Build and Push Docker Images / build-backend (push) Successful in 50s
Build and Push Docker Images / build-website (push) Successful in 22s
Build and Push Docker Images / build-central-services-api (push) Successful in 22s
Build and Push Docker Images / build-central-services-admin (push) Successful in 22s
Build and Push Docker Images / build-docs (push) Successful in 21s
Build and Push Docker Images / build-frontend (push) Successful in 22s

This commit is contained in:
root
2026-08-31 10:58:33 +00:00
parent 6b5d4f7f36
commit 02ab26771d
4 changed files with 412 additions and 0 deletions

View File

@@ -4,16 +4,26 @@ import {
accounts,
bankstatements,
createddocuments,
customers,
filetags,
files,
folders,
incominginvoices,
ownaccounts,
statementallocations,
vendors,
} from "../../../db/schema"
import { useNextNumberRangeNumber } from "../../utils/functions"
import { saveFile } from "../../utils/files"
import { insertHistoryItem } from "../../utils/history"
import {
prepareStatementAllocationInput,
statementAllocationUuidArg,
} from "../statementAllocations"
import { McpTool } from "../types"
export { prepareStatementAllocationInput } from "../statementAllocations"
const limitFromArgs = (args: Record<string, unknown>, fallback = 25) => {
const raw = Number(args.limit ?? fallback)
if (!Number.isFinite(raw)) return fallback
@@ -357,6 +367,43 @@ const validateIncomingInvoiceData = (invoice: Record<string, any>) => {
}
}
const assertTenantEntityExists = async (
context: any,
table: any,
idColumn: any,
tenantColumn: any | null,
id: unknown,
label: string,
) => {
if (!hasValue(id)) return
const [row] = await context.server.db
.select({ id: idColumn })
.from(table)
.where(tenantColumn
? and(eq(idColumn, id as any), eq(tenantColumn, context.tenantId))
: eq(idColumn, id as any))
.limit(1)
if (!row) throw new Error(`${label} nicht gefunden`)
}
const assertStatementAllocationReferences = async (context: any, payload: Record<string, any>) => {
await Promise.all([
assertTenantEntityExists(context, bankstatements, bankstatements.id, bankstatements.tenant, payload.bankstatement, "Bankumsatz"),
assertTenantEntityExists(context, createddocuments, createddocuments.id, createddocuments.tenant, payload.createddocument, "Ausgangsbeleg"),
assertTenantEntityExists(context, incominginvoices, incominginvoices.id, incominginvoices.tenant, payload.incominginvoice, "Eingangsbeleg"),
assertTenantEntityExists(context, accounts, accounts.id, null, payload.account, "Sachkonto"),
assertTenantEntityExists(context, accounts, accounts.id, null, payload.contraAccount, "Haben-Sachkonto"),
assertTenantEntityExists(context, ownaccounts, ownaccounts.id, ownaccounts.tenant, payload.ownaccount, "Zusätzliches Konto"),
assertTenantEntityExists(context, ownaccounts, ownaccounts.id, ownaccounts.tenant, payload.contraOwnaccount, "Zusätzliches Haben-Konto"),
assertTenantEntityExists(context, customers, customers.id, customers.tenant, payload.customer, "Debitor"),
assertTenantEntityExists(context, customers, customers.id, customers.tenant, payload.contraCustomer, "Haben-Debitor"),
assertTenantEntityExists(context, vendors, vendors.id, vendors.tenant, payload.vendor, "Kreditor"),
assertTenantEntityExists(context, vendors, vendors.id, vendors.tenant, payload.contraVendor, "Haben-Kreditor"),
])
}
export const accountingTools: McpTool[] = [
{
name: "accounting.outgoing_documents.tax_types.list",
@@ -1175,4 +1222,113 @@ export const accountingTools: McpTool[] = [
return { rows }
},
},
{
name: "accounting.statement_allocations.create",
title: "Bankzuweisung erstellen",
description: "Erstellt eine Bankzuweisung oder manuelle Soll/Haben-Buchung. Unterstützt Ausgangs- und Eingangsbelege, Sachkonten, zusätzliche Konten, Debitoren, Kreditoren, Teilbeträge, DATEV-Steuerschlüssel und Abschreibungen.",
requiredPermissions: ["accounting.statement_allocations.write"],
inputSchema: {
type: "object",
required: ["amount"],
properties: {
bankstatement: { type: "number", description: "Bankumsatz-ID. Weglassen für eine manuelle Soll/Haben-Buchung." },
amount: { type: "number", description: "Zuweisungsbetrag mit Vorzeichen des Bankumsatzes; bei manuellen Buchungen positiv." },
createddocument: { type: "number", description: "Ausgangsbeleg-ID." },
incominginvoice: { type: "number", description: "Eingangsbeleg-ID." },
account: { type: "number", description: "Sachkonto-ID beziehungsweise Soll-Sachkonto." },
ownaccount: { type: "string", description: "UUID eines zusätzlichen Kontos beziehungsweise Soll-Kontos." },
customer: { type: "number", description: "Debitor-ID beziehungsweise Soll-Debitor." },
vendor: { type: "number", description: "Kreditor-ID beziehungsweise Soll-Kreditor." },
contraAccount: { type: "number", description: "Haben-Sachkonto für manuelle Buchungen." },
contraOwnaccount: { type: "string", description: "UUID eines zusätzlichen Haben-Kontos." },
contraCustomer: { type: "number", description: "Haben-Debitor für manuelle Buchungen." },
contraVendor: { type: "number", description: "Haben-Kreditor für manuelle Buchungen." },
manualBookingDate: { type: "string", format: "date", description: "Buchungsdatum einer manuellen Buchung." },
manualInvoiceSide: { type: "string", enum: ["debit", "credit"], description: "Soll/Haben-Seite eines Eingangsbelegs in einer manuellen Buchung." },
description: { type: "string" },
datevTaxKey: { type: "string", description: "Optionaler DATEV-Steuerschlüssel, z. B. 9, 8, 19 oder 18." },
bookingMode: { type: "string", enum: ["expense", "depreciation_single", "depreciation_bundle"], default: "expense" },
depreciationMonths: { type: "number", minimum: 1 },
depreciationStartDate: { type: "string", format: "date" },
depreciationMethod: { type: "string", enum: ["linear", "degressive"], default: "linear" },
depreciationLabel: { type: "string" },
depreciationGroup: { type: "string" },
residualValue: { type: "number", minimum: 0, default: 0 },
},
},
async handler(context, args) {
const payload = prepareStatementAllocationInput(args)
await assertStatementAllocationReferences(context, payload)
const [created] = await context.server.db
.insert(statementallocations)
.values({
...payload,
tenant: context.tenantId,
updated_at: new Date(),
updated_by: context.userId,
})
.returning()
if (created?.bankstatement) {
await insertHistoryItem(context.server, {
entity: "bankstatements",
entityId: Number(created.bankstatement),
action: "created",
created_by: context.userId,
tenant_id: context.tenantId,
oldVal: null,
newVal: created,
text: "Buchung über MCP erstellt",
})
}
return { allocation: created }
},
},
{
name: "accounting.statement_allocations.delete",
title: "Bankzuweisung löschen",
description: "Löscht eine Bankzuweisung oder manuelle Soll/Haben-Buchung im aktiven Mandanten.",
requiredPermissions: ["accounting.statement_allocations.write"],
inputSchema: {
type: "object",
required: ["id"],
properties: {
id: { type: "string", description: "UUID der Bankzuweisung." },
},
},
async handler(context, args) {
const id = statementAllocationUuidArg(args, "id")
if (!id) throw new Error("id ist erforderlich")
const [existing] = await context.server.db
.select()
.from(statementallocations)
.where(and(eq(statementallocations.id, id), eq(statementallocations.tenant, context.tenantId)))
.limit(1)
if (!existing) throw new Error("Bankzuweisung nicht gefunden")
const [deleted] = await context.server.db
.delete(statementallocations)
.where(and(eq(statementallocations.id, id), eq(statementallocations.tenant, context.tenantId)))
.returning()
if (existing.bankstatement) {
await insertHistoryItem(context.server, {
entity: "bankstatements",
entityId: Number(existing.bankstatement),
action: "deleted",
created_by: context.userId,
tenant_id: context.tenantId,
oldVal: existing,
newVal: null,
text: "Buchung über MCP gelöscht",
})
}
return { deleted: true, allocation: deleted }
},
},
]