Files
FireAdministration/tests/unit/kasse-betrag.test.ts
T

154 lines
5.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Prisma } from "@prisma/client";
import { describe, expect, it } from "vitest";
import {
alsText,
alsWirkung,
bestand,
betrag,
betragLesen,
bilanz,
positionssumme,
runden,
summe,
} from "@/lib/kasse/betrag";
/**
* Das Rechnen mit Geld.
*
* Der Grund für diese Datei ist eine einzige Zeile, die es hier nicht geben
* darf: `0.1 + 0.2`. In Fließkomma ergibt das 0.30000000000000004, und in
* einer Kasse ist das kein Rundungsfehler, sondern eine Differenz in der
* Kassenprüfung, die niemand erklären kann. Deshalb rechnet die gesamte
* Kasse mit `Prisma.Decimal`, und deshalb prüft dieser Test genau das nach.
*/
describe("Beträge lesen", () => {
it("nimmt Zahl, Text und Decimal entgegen", () => {
expect(betrag(12.5).toFixed(2)).toBe("12.50");
expect(betrag("12.50").toFixed(2)).toBe("12.50");
expect(betrag(new Prisma.Decimal("12.5")).toFixed(2)).toBe("12.50");
});
it("macht aus fehlenden Angaben null Euro", () => {
expect(betrag(null).toFixed(2)).toBe("0.00");
expect(betrag(undefined).toFixed(2)).toBe("0.00");
expect(betrag("").toFixed(2)).toBe("0.00");
});
});
describe("Summe", () => {
it("rechnet ohne Fließkommafehler", () => {
// Die Probe aufs Exempel: in `number` ergäbe das 0.30000000000000004.
expect(summe(["0.10", "0.20"]).toFixed(2)).toBe("0.30");
});
it("addiert eine ganze Kassenseite auf den Cent", () => {
const werte = ["1284.50", "412.90", "2340.00", "868.00", "1150.00", "624.00", "398.20", "36.00"];
expect(summe(werte).toFixed(2)).toBe("7113.60");
});
it("ergibt bei leerer Liste null Euro und nicht undefined", () => {
expect(summe([]).toFixed(2)).toBe("0.00");
});
});
describe("Runden", () => {
it("rundet kaufmännisch — ab der halben Stelle nach oben", () => {
expect(runden(new Prisma.Decimal("2.345")).toFixed(2)).toBe("2.35");
expect(runden(new Prisma.Decimal("2.344")).toFixed(2)).toBe("2.34");
// Und nicht bankers rounding: 2,355 wird 2,36, nicht 2,36-oder-2,35.
expect(runden(new Prisma.Decimal("2.355")).toFixed(2)).toBe("2.36");
expect(runden(new Prisma.Decimal("2.365")).toFixed(2)).toBe("2.37");
});
});
describe("Richtung", () => {
it("macht aus Art und positivem Betrag ein Vorzeichen", () => {
expect(alsWirkung("EINNAHME", "100.00").toFixed(2)).toBe("100.00");
expect(alsWirkung("AUSGABE", "100.00").toFixed(2)).toBe("-100.00");
});
});
describe("Bestand", () => {
it("ist Anfangsbestand plus die Wirkung aller Buchungen", () => {
const wert = bestand("250.00", [
{ art: "EINNAHME", betrag: "3860.00" },
{ art: "AUSGABE", betrag: "1420.60" },
]);
expect(wert.toFixed(2)).toBe("2689.40");
});
it("darf negativ werden — eine überzogene Barkasse ist ein Befund, kein Fehler", () => {
expect(bestand("10.00", [{ art: "AUSGABE", betrag: "50.00" }]).toFixed(2)).toBe("-40.00");
});
});
describe("Bilanz", () => {
it("trennt Einnahmen und Ausgaben und bildet den Saldo", () => {
const werte = bilanz([
{ art: "EINNAHME", betrag: "6500.00" },
{ art: "EINNAHME", betrag: "780.00" },
{ art: "AUSGABE", betrag: "1284.50" },
]);
expect(werte.einnahmen.toFixed(2)).toBe("7280.00");
expect(werte.ausgaben.toFixed(2)).toBe("1284.50");
expect(werte.saldo.toFixed(2)).toBe("5995.50");
});
});
describe("Positionssumme", () => {
it("rechnet Viertelstunden auf den Cent", () => {
// 0,25 × 47,50 = 11,875 — gerundet 11,88 und nicht 11,87.
expect(positionssumme("0.25", "47.50").toFixed(2)).toBe("11.88");
});
it("rechnet ganze Mengen ohne Rest", () => {
expect(positionssumme(9, "24.00").toFixed(2)).toBe("216.00");
});
});
describe("Eingaben lesen", () => {
it("versteht die deutsche Schreibweise", () => {
expect(betragLesen("1.234,56")?.toFixed(2)).toBe("1234.56");
expect(betragLesen("1.234,56 €")?.toFixed(2)).toBe("1234.56");
expect(betragLesen("12,50 EUR")?.toFixed(2)).toBe("12.50");
});
it("versteht die englische Schreibweise", () => {
expect(betragLesen("1,234.56")?.toFixed(2)).toBe("1234.56");
expect(betragLesen("$1234.56")?.toFixed(2)).toBe("1234.56");
});
it("liest auch Beträge ohne Trennzeichen", () => {
expect(betragLesen("42")?.toFixed(2)).toBe("42.00");
expect(betragLesen("-42,50")?.toFixed(2)).toBe("-42.50");
});
it("gibt bei Unlesbarem null zurück — und nicht null Euro", () => {
// Der Unterschied ist der ganze Punkt: null Euro ist eine Angabe,
// unlesbar ist eine Rückfrage an den, der es eingetippt hat.
expect(betragLesen("etwa zwanzig")).toBeNull();
expect(betragLesen("")).toBeNull();
expect(betragLesen("1,2,3")).toBeNull();
});
});
describe("Beträge schreiben", () => {
it("setzt Tausenderpunkte und ein Komma", () => {
expect(alsText("1234.56")).toBe("1.234,56 €");
expect(alsText("12.50")).toBe("12,50 €");
expect(alsText("1234567.89")).toBe("1.234.567,89 €");
});
it("verwendet das typografische Minus", () => {
expect(alsText("-3.20")).toBe("−3,20 €");
});
it("macht aus fehlenden Angaben einen Gedankenstrich", () => {
expect(alsText(null)).toBe("—");
expect(alsText(undefined)).toBe("—");
});
});