201 lines
6.4 KiB
TypeScript
201 lines
6.4 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
STANDARDKATEGORIEN,
|
|
aehnlichkeit,
|
|
kategorieVorschlagen,
|
|
woerter,
|
|
} from "@/lib/kasse/vorschlag";
|
|
|
|
/**
|
|
* Der Kategorievorschlag.
|
|
*
|
|
* Sein Nutzen liegt nicht in den gesparten Sekunden, sondern darin, dass
|
|
* dieselbe Sache immer unter derselben Kategorie landet — erst das macht die
|
|
* Jahresauswertung brauchbar. Zwei Kategorien für Diesel, weil einmal
|
|
* „Kraftstoff" und einmal „Fahrzeuge" gewählt wurde, sind in der
|
|
* Jahreshauptversammlung nicht zu erklären.
|
|
*
|
|
* Ebenso wichtig ist, wann er schweigt: ein falscher Vorschlag wird
|
|
* übernommen, ohne gelesen zu werden. Deshalb die Schwelle bei der Hälfte
|
|
* gemeinsamer Wörter — und deshalb prüft dieser Test beide Seiten.
|
|
*/
|
|
|
|
const KATEGORIEN = [
|
|
{ id: "k-kraftstoff", name: "Kraftstoff", art: "AUSGABE" as const },
|
|
{ id: "k-reparatur", name: "Reparatur", art: "AUSGABE" as const },
|
|
{ id: "k-strom", name: "Wasser und Strom", art: "AUSGABE" as const },
|
|
{ id: "k-zuschuss", name: "Zuschüsse", art: "EINNAHME" as const },
|
|
{ id: "k-beitrag", name: "Mitgliedsbeiträge", art: "EINNAHME" as const },
|
|
];
|
|
|
|
describe("Wörter", () => {
|
|
it("wirft Füllwörter, kurze Wörter und reine Zahlen weg", () => {
|
|
expect(woerter("Rechnung Nr. 4711 vom 12.03. für die Feuerwehr GmbH")).toEqual([]);
|
|
});
|
|
|
|
it("behält, was unterscheidet", () => {
|
|
expect(woerter("Tankstelle Musterhausen Diesel LF 20")).toEqual([
|
|
"tankstelle",
|
|
"musterhausen",
|
|
"diesel",
|
|
]);
|
|
});
|
|
|
|
it("lässt Umlaute stehen", () => {
|
|
expect(woerter("Prüfung Drehleiter")).toEqual(["prüfung", "drehleiter"]);
|
|
});
|
|
});
|
|
|
|
describe("Ähnlichkeit", () => {
|
|
it("misst am kürzeren der beiden Texte", () => {
|
|
// "stadtwerke musterhausen" steckt vollständig in beiden.
|
|
const wert = aehnlichkeit(
|
|
"Stadtwerke Musterhausen Abschlag Strom",
|
|
"Stadtwerke Musterhausen Jahresabrechnung Strom und Wasser",
|
|
);
|
|
expect(wert).toBeGreaterThanOrEqual(0.5);
|
|
});
|
|
|
|
it("ergibt null bei nichts Gemeinsamem", () => {
|
|
expect(aehnlichkeit("Diesel Tankstelle", "Einsatzjacken Beschaffung")).toBe(0);
|
|
});
|
|
|
|
it("ergibt null, wenn nach dem Aussieben nichts übrig ist", () => {
|
|
expect(aehnlichkeit("Rechnung vom", "Rechnung Nr. 12")).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe("Vorschlag aus dem eigenen Bestand", () => {
|
|
const vorbilder = [
|
|
{
|
|
kategorieId: "k-kraftstoff",
|
|
kategorieName: "Kraftstoff",
|
|
verwendungszweck: "Tankstelle Musterhausen Diesel LF 20 Sammelrechnung",
|
|
art: "AUSGABE" as const,
|
|
},
|
|
{
|
|
kategorieId: "k-reparatur",
|
|
kategorieName: "Reparatur",
|
|
verwendungszweck: "Werkstatt Müller Kupplung MTW",
|
|
art: "AUSGABE" as const,
|
|
},
|
|
];
|
|
|
|
it("nimmt die Kategorie, unter der dasselbe zuletzt gebucht wurde", () => {
|
|
const vorschlag = kategorieVorschlagen(
|
|
"Tankstelle Musterhausen Diesel MTW",
|
|
"AUSGABE",
|
|
vorbilder,
|
|
KATEGORIEN,
|
|
);
|
|
|
|
expect(vorschlag?.kategorieId).toBe("k-kraftstoff");
|
|
expect(vorschlag?.grund).toMatch(/zuletzt so gebucht/);
|
|
});
|
|
|
|
it("geht dem Bestand vor den Stichwörtern nach — die Wehr kennt sich selbst am besten", () => {
|
|
const eigen = [
|
|
{
|
|
kategorieId: "k-strom",
|
|
kategorieName: "Wasser und Strom",
|
|
// Diese Wehr bucht Diesel unter Wasser und Strom. Merkwürdig, aber
|
|
// ihre Entscheidung — und sie soll dabei bleiben dürfen.
|
|
verwendungszweck: "Tankstelle Musterhausen Diesel LF 20",
|
|
art: "AUSGABE" as const,
|
|
},
|
|
];
|
|
|
|
const vorschlag = kategorieVorschlagen(
|
|
"Tankstelle Musterhausen Diesel LF 20",
|
|
"AUSGABE",
|
|
eigen,
|
|
KATEGORIEN,
|
|
);
|
|
|
|
expect(vorschlag?.kategorieId).toBe("k-strom");
|
|
});
|
|
|
|
it("schweigt bei zufälliger Übereinstimmung, statt zu raten", () => {
|
|
const vorschlag = kategorieVorschlagen(
|
|
"Möbelhaus Sonnleitner Tischbeschaffung Schulungsraum",
|
|
"AUSGABE",
|
|
vorbilder,
|
|
KATEGORIEN,
|
|
);
|
|
|
|
expect(vorschlag).toBeNull();
|
|
});
|
|
|
|
it("mischt Einnahmen und Ausgaben nicht", () => {
|
|
const vorschlag = kategorieVorschlagen(
|
|
"Tankstelle Musterhausen Diesel LF 20 Sammelrechnung",
|
|
"EINNAHME",
|
|
vorbilder,
|
|
KATEGORIEN,
|
|
);
|
|
|
|
// Der Vorgänger ist eine Ausgabe und kommt deshalb nicht in Frage; ein
|
|
// Stichwort für Einnahmen trifft hier ebenso wenig.
|
|
expect(vorschlag?.kategorieId).not.toBe("k-kraftstoff");
|
|
});
|
|
});
|
|
|
|
describe("Vorschlag aus Stichwörtern", () => {
|
|
it("greift, solange es keinen Bestand gibt", () => {
|
|
const vorschlag = kategorieVorschlagen("Diesel für das LF 20", "AUSGABE", [], KATEGORIEN);
|
|
|
|
expect(vorschlag?.kategorieId).toBe("k-kraftstoff");
|
|
expect(vorschlag?.grund).toMatch(/Verwendungszweck/);
|
|
// Niedrigere Sicherheit als ein Treffer aus dem Bestand: ein Stichwort
|
|
// weiß nichts über die Gepflogenheiten dieser Wehr.
|
|
expect(vorschlag?.sicherheit).toBeLessThan(1);
|
|
});
|
|
|
|
it("erkennt den Gemeindezuschuss als Einnahme", () => {
|
|
const vorschlag = kategorieVorschlagen(
|
|
"Zuschuss der Gemeinde für den laufenden Betrieb",
|
|
"EINNAHME",
|
|
[],
|
|
KATEGORIEN,
|
|
);
|
|
|
|
expect(vorschlag?.kategorieId).toBe("k-zuschuss");
|
|
});
|
|
|
|
it("schlägt nichts vor, wenn es die Kategorie in dieser Wehr nicht gibt", () => {
|
|
const vorschlag = kategorieVorschlagen("Neue Einsatzjacken", "AUSGABE", [], KATEGORIEN);
|
|
expect(vorschlag).toBeNull();
|
|
});
|
|
|
|
it("schweigt bei leerem Verwendungszweck", () => {
|
|
expect(kategorieVorschlagen(" ", "AUSGABE", [], KATEGORIEN)).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("Standardkontenrahmen", () => {
|
|
it("verweist nur auf Oberkategorien, die es auch gibt", () => {
|
|
const namen = new Set(STANDARDKATEGORIEN.map((eintrag) => eintrag.name));
|
|
|
|
for (const eintrag of STANDARDKATEGORIEN) {
|
|
if (eintrag.oberkategorie) {
|
|
expect(namen.has(eintrag.oberkategorie)).toBe(true);
|
|
}
|
|
}
|
|
});
|
|
|
|
it("gibt jeden Namen nur einmal — die Datenbank verlangt es so", () => {
|
|
const namen = STANDARDKATEGORIEN.map((eintrag) => eintrag.name);
|
|
expect(new Set(namen).size).toBe(namen.length);
|
|
});
|
|
|
|
it("stellt jede Unterkategorie in eine Oberkategorie derselben Richtung", () => {
|
|
const nachName = new Map(STANDARDKATEGORIEN.map((eintrag) => [eintrag.name, eintrag]));
|
|
|
|
for (const eintrag of STANDARDKATEGORIEN) {
|
|
if (eintrag.oberkategorie) {
|
|
expect(nachName.get(eintrag.oberkategorie)?.art).toBe(eintrag.art);
|
|
}
|
|
}
|
|
});
|
|
});
|