import assert from "node:assert/strict" import test from "node:test" import { PgDialect } from "drizzle-orm/pg-core" import { normalizeBankAllocationDetails, validateBankAllocationCostCentre } from "../src/modules/bank-allocation-details" import { prepareStatementAllocationInput } from "../src/mcp/statementAllocations" import bankingRoutes from "../src/routes/banking" import { statementallocations } from "../db/schema" const costcentre = "57d31d62-11a2-47c5-b074-69f9c5ab8bba" test("keeps cost centre and VAT key on the gross bank allocation through input preparation", () => { const prepared = prepareStatementAllocationInput({ bankstatement: 42, account: 13, amount: -119, costcentre, datevTaxKey: " 9 " }) assert.equal(prepared.amount, -119) assert.equal(prepared.costcentre, costcentre) assert.equal(prepared.datevTaxKey, "9") assert.deepEqual(normalizeBankAllocationDetails({ amount: -119, account: 13, bankstatement: 42 }), { costcentre: null, datevTaxKey: null }) }) test("rejects invalid cost centres, ambiguous document assignments and malformed tax keys", () => { assert.throws(() => normalizeBankAllocationDetails({ costcentre: "invalid" }), /UUID/) assert.throws(() => normalizeBankAllocationDetails({ costcentre, bankstatement: 1, account: 13, incominginvoice: 2 }), /direkte Bankbuchungen/) assert.throws(() => normalizeBankAllocationDetails({ datevTaxKey: '9";evil' }), /Steuerschlüssel/) }) test("cost centre lookup requires the active tenant and excludes archived centres", async () => { let condition: any const query = { from: () => query, where: (value: any) => { condition = value; return query }, limit: async () => [] } await assert.rejects(validateBankAllocationCostCentre({ select: () => query }, 7, costcentre), /nicht gefunden oder ist archiviert/) const sql = new PgDialect().sqlToQuery(condition) assert.match(sql.sql, /"costcentres"\."tenant"/) assert.match(sql.sql, /"costcentres"\."archived"/) assert.deepEqual(sql.params, [costcentre, 7, false]) await validateBankAllocationCostCentre({ select: () => ({ ...query, from: () => ({ where: () => ({ limit: async () => [{ id: costcentre }] }) }) }) }, 7, costcentre) await validateBankAllocationCostCentre({}, 7, null) }) test("banking API persists the gross allocation with cost centre and VAT, and rejects unknown centres before writing", async () => { const handlers = new Map() const saved: any[] = [] let centreExists = true const lookup: any = { from: () => lookup, where: () => lookup, limit: async () => centreExists ? [{ id: costcentre }] : [] } const server: any = { get() {}, patch() {}, delete() {}, post: (url: string, handler: any) => handlers.set(url, handler), db: { select: () => lookup, insert: (table: any) => ({ values: (value: any) => { if (table === statementallocations) saved.push(value) return { returning: async () => [{ id: "allocation", ...value }] } } }), }, } await bankingRoutes(server) const handler = handlers.get("/banking/statements") const request = { user: { tenant_id: 7, user_id: "user" }, body: { data: { bankstatement: 42, account: 13, amount: -119, costcentre, datevTaxKey: "9" } } } let status = 200 let response: any const reply = { code: (code: number) => { status = code; return reply }, send: (body: any) => { response = body; return reply } } await handler(request, reply) assert.equal(status, 200) assert.equal(response.amount, -119) assert.equal(response.costcentre, costcentre) assert.equal(response.datevTaxKey, "9") assert.equal(saved[0].tenant, 7) centreExists = false await handler(request, reply) assert.equal(status, 400) assert.equal(saved.length, 1) })