import { describe, it, expect } from "vitest"; import { TAX_RATES, calcItemAmounts, calcItemAmountsKleinunternehmer, calcInvoiceTotals, formatCurrency, formatDate, } from "@/lib/tax"; describe("tax.ts - German Tax Calculations", () => { describe("TAX_RATES", () => { it("should have correct tax rate values", () => { expect(TAX_RATES).toEqual([ { label: "19% MwSt. (Regelsteuersatz)", value: 19 }, { label: "7% MwSt. (ermäßigt)", value: 7 }, { label: "0% (steuerfrei / §13b UStG)", value: 0 }, ]); }); }); describe("calcItemAmounts", () => { it("should calculate correct amounts for 19% tax", () => { const result = calcItemAmounts(2, 100, 19); expect(result).toEqual({ netAmount: 200, taxAmount: 38, grossAmount: 238, }); }); it("should calculate correct amounts for 7% tax", () => { const result = calcItemAmounts(1, 50, 7); expect(result).toEqual({ netAmount: 50, taxAmount: 3.5, grossAmount: 53.5, }); }); it("should handle tax-free items (0%)", () => { const result = calcItemAmounts(3, 33.33, 0); expect(result).toEqual({ netAmount: 99.99, taxAmount: 0, grossAmount: 99.99, }); }); it("should handle decimal quantities", () => { const result = calcItemAmounts(1.5, 100, 19); expect(result).toEqual({ netAmount: 150, taxAmount: 28.5, grossAmount: 178.5, }); }); it("should round to 2 decimal places", () => { const result = calcItemAmounts(1, 33.333, 19); // 33.333 * 19% = 6.333..., rounded to 6.33 expect(result.netAmount).toBe(33.33); expect(result.taxAmount).toBe(6.33); expect(result.grossAmount).toBe(39.66); }); }); describe("calcItemAmountsKleinunternehmer", () => { it("should set tax to 0 for Kleinunternehmer", () => { const result = calcItemAmountsKleinunternehmer(2, 100); expect(result).toEqual({ netAmount: 200, taxAmount: 0, grossAmount: 200, }); }); it("should treat gross as net for Kleinunternehmer", () => { const result = calcItemAmountsKleinunternehmer(1, 50); expect(result.netAmount).toBe(50); expect(result.grossAmount).toBe(50); }); }); describe("calcInvoiceTotals", () => { it("should sum up multiple invoice items", () => { const items = [ { netAmount: 100, taxAmount: 19, grossAmount: 119 }, { netAmount: 50, taxAmount: 3.5, grossAmount: 53.5 }, ]; const result = calcInvoiceTotals(items); expect(result).toEqual({ netTotal: 150, taxTotal: 22.5, grossTotal: 172.5, }); }); it("should handle empty items array", () => { const result = calcInvoiceTotals([]); expect(result).toEqual({ netTotal: 0, taxTotal: 0, grossTotal: 0, }); }); it("should round totals to 2 decimal places", () => { const items = [ { netAmount: 33.333, taxAmount: 6.333, grossAmount: 39.666 }, { netAmount: 66.666, taxAmount: 12.666, grossAmount: 79.332 }, ]; const result = calcInvoiceTotals(items); // calcInvoiceTotals uses Math.round which rounds 99.999 to 100 expect(result.netTotal).toBe(100); expect(result.taxTotal).toBe(19); expect(result.grossTotal).toBe(119); }); it("should handle Kleinunternehmer invoice (no tax)", () => { const items = [ { netAmount: 100, taxAmount: 0, grossAmount: 100 }, { netAmount: 200, taxAmount: 0, grossAmount: 200 }, ]; const result = calcInvoiceTotals(items); expect(result).toEqual({ netTotal: 300, taxTotal: 0, grossTotal: 300, }); }); }); describe("formatCurrency", () => { it("should format number to EUR currency", () => { // Note: Intl.NumberFormat uses non-breaking space (\u00A0) before € expect(formatCurrency(1234.56)).toBe("1.234,56\u00A0€"); }); it("should handle string input", () => { expect(formatCurrency("999.99")).toBe("999,99\u00A0€"); }); it("should format zero correctly", () => { expect(formatCurrency(0)).toBe("0,00\u00A0€"); }); }); describe("formatDate", () => { it("should format Date object to German format", () => { const date = new Date("2026-05-08"); expect(formatDate(date)).toBe("8.5.2026"); }); it("should format date string to German format", () => { expect(formatDate("2026-12-31")).toBe("31.12.2026"); }); }); });