feat: CNB exchange rates, multi-currency KPI stats, invoice PDF VAT in CZK

- ČNB exchange rate service with date-specific rates and caching
- Invoice/received invoice stats convert foreign currencies to CZK
- Dashboard revenue converts all currencies to CZK
- Invoice PDF: VAT recap table always in CZK with CNB rate footer
- Inline styles replaced with utility classes (step 4 cleanup)
- Spinner animation exempt from prefers-reduced-motion

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
BOHA
2026-03-27 13:44:53 +01:00
parent a3ef37d0d2
commit 8cdf057ab3
13 changed files with 173 additions and 117 deletions

View File

@@ -3,6 +3,7 @@ import prisma from "../../config/database";
import { requireAuth } from "../../middleware/auth";
import { success } from "../../utils/response";
import { localTimeStr } from "../../utils/date";
import { toCzk } from "../../services/exchange-rates";
export default async function dashboardRoutes(
fastify: FastifyInstance,
@@ -206,10 +207,13 @@ export default async function dashboardRoutes(
}),
),
unpaid_count: unpaidCount,
revenue_czk:
revenueByCurrency["CZK"] != null
? Math.round(revenueByCurrency["CZK"] * 100) / 100
: null,
revenue_czk: await (async () => {
let total = 0;
for (const [cur, amount] of Object.entries(revenueByCurrency)) {
total += await toCzk(Math.round(amount * 100) / 100, cur);
}
return Math.round(total * 100) / 100;
})(),
};
result.unpaid_invoices = unpaidCount;
}