feat(odin): received-invoices expense totals with CZK conversion

get_document_totals gains type=received_invoices: whole-set gross sums per
currency + total_czk via the same toCzk logic as the stats route (unknown
currencies skipped + reported), month/year via the literal columns (bare
year supported), unpaid/paid filter, period echo. invoices.view joined the
coarse gate (the per-type re-check keeps other families locked); the tool
description routes 'kolik jsme utratili' questions here.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
BOHA
2026-06-12 09:34:51 +02:00
parent 5ec70599aa
commit 6877919133
2 changed files with 94 additions and 9 deletions

View File

@@ -765,6 +765,34 @@ describe("ai-tools — employees, attendance detail, trips, work plan", () => {
);
});
it("get_document_totals: received_invoices sums gross + converts to CZK", async () => {
// invoices.view alone unlocks the received-invoices branch.
const res = await executeTool(
"get_document_totals",
{ type: "received_invoices", month: 6, year: 2098 },
VIEWER_INVOICES,
);
expect(res.ok).toBe(true);
const r = res.result as {
period: string;
invoice_count: number;
totals: { currency: string; amount: number }[];
total_czk: number;
};
expect(r.period).toBe("6/2098");
expect(r.invoice_count).toBe(1);
expect(r.totals).toEqual([{ currency: "CZK", amount: 1210 }]);
expect(r.total_czk).toBe(1210); // CZK rows convert 1:1, no rate fetch
// orders.view alone cannot read expense totals.
const denied = await executeTool(
"get_document_totals",
{ type: "received_invoices" },
{ userId: 999999998, roleName: "viewer", permissions: ["orders.view"] },
);
expect(denied.ok).toBe(false);
});
it("get_top_customers aggregates per customer across the whole table", async () => {
const res = await executeTool(
"get_top_customers",