Files
alpenwerk-hr/lib/employee-search.ts
Andrei Laas cf5ed5c6c6
Some checks failed
CI / Migrationen auf leerer Datenbank (push) Has been cancelled
CI / Lint, Typen, Tests, Build (push) Successful in 11m17s
Vier Befunde, die still falsche Ergebnisse lieferten
C.08 -- die Suche mit Bindestrich fand nichts. Das Feld wird per translate an
"-/:.," in Leerzeichen zerlegt, die Eingabe aber nicht: "Mueller-Weiss" erzeugte
das Muster mueller-weiss%, waehrend im Heuhaufen "mueller weiss" stand. Ohne
Bindestrich fand man dieselbe Person. Die Trennzeichen stehen jetzt einmal in
lib/employee-search.ts und werden von beiden Seiten des Vergleichs benutzt.

C.09 -- der Einheitenfilter verlor jede Person mit vorgemerktem Austritt.
"Laufend" war valid_to is null, aber terminate_employee setzt das Ende schon
beim Erfassen, Monate vor dem Tag. Jetzt zaehlt auch, was noch laeuft
(valid_to > heute). Bewusst ohne valid_from <= heute: ein geplanter Eintritt
gehoert in die Liste, sonst fiele er aus dem Filter, obwohl der Status
"Geplant" ihn ausdruecklich fuehrt.

E.07 -- nur die Kostenstelle zu aendern war unmoeglich. update_position weist
einen Aufruf ohne Aenderung ab, und die Umkontierung lief erst bei dessen
Erfolg. Sie wird jetzt uebersprungen, wenn sich an den Stammangaben nichts
geaendert hat.

H.06 -- jedes Speichern erzeugte zusaetzlich "Wochenstunden 30.0 -> 30". Der
Vergleich laeuft ueber Text, die Spalte ist numeric(4,1), und das Formular
schickt 30. Der Kommentar an der Zeile nannte die Absicht richtig, nur reicht
::numeric dafuer nicht -- es muss auf die Genauigkeit der Spalte gehen.
2026-09-29 19:47:48 +02:00

57 lines
2.4 KiB
TypeScript

// Wie aus einer Eingabe Suchmuster werden.
//
// Getrennt von der Seite, weil hier die Entscheidungen stecken, die man
// prüfen können muss: was als Wort zählt, was am Wortanfang treffen muss und
// was von LIKE als Text und nicht als Platzhalter gelesen wird.
//
// Die Bedeutung selbst — „trifft am Wortanfang" — liegt im SQL der Seite:
// verglichen wird gegen Vorname, Nachname und Position zusammengesetzt, mit
// Trennzeichen als Wortgrenze.
/**
* Die Zeichen, die im Namen als Wortgrenze zählen.
*
* Sie stehen hier und nicht in der Seite, weil **beide Seiten des Vergleichs**
* sie brauchen: das Feld wird per `translate` an diesen Zeichen in Leerzeichen
* zerlegt, und die Eingabe muss genauso zerlegt werden. Vorher tat es nur das
* Feld — wer „Müller-Weiß" mit Bindestrich suchte, erzeugte das Muster
* `müller-weiß%`, während im Heuhaufen „müller weiß" stand. Null Treffer,
* obwohl die Person da ist; ohne Bindestrich fand man sie. Gemeldet im Test
* vom 29.09. (C.08).
*/
export const TRENNZEICHEN = "-/:.,";
/** Für `translate(…, TRENNZEICHEN, TRENNZEICHEN_ERSATZ)`: gleich lang, nur Leerzeichen. */
export const TRENNZEICHEN_ERSATZ = " ".repeat(TRENNZEICHEN.length);
const WORTGRENZE = new RegExp(`[\\s${TRENNZEICHEN.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&")}]+`);
/** Nur Ziffern? Dann ist es eine Personalnummer und kein Name. */
export function istPersonalnummer(term: string): boolean {
return /^\d+$/.test(term.trim());
}
/**
* Je Suchwort zwei LIKE-Muster: „am Anfang" und „nach einem Leerzeichen".
* Zusammen ergeben sie „am Anfang eines Wortes".
*
* Ein Wort als Teilzeichenkette zu suchen wäre einfacher und war die erste
* Fassung — bei „Winkler H" traf das „H" dann auf Thomas, Katharina und
* CNC-Dreher:in, also auf alle. Je kürzer die Eingabe, desto unbrauchbarer
* das Ergebnis, und ein Anfangsbuchstabe ist die kürzeste sinnvolle Eingabe.
*/
export function suchMuster(term: string): string[][] {
return term
.trim()
// An denselben Zeichen zerlegt wie das Feld, gegen das verglichen wird:
// „Müller-Weiß" wird zu zwei Wörtern, die beide am Wortanfang treffen
// müssen — genau wie „Müller Weiß".
.split(WORTGRENZE)
.filter(Boolean)
.map((wort) => {
// Die Platzhalter von LIKE entschärfen: wer „50 %" tippt, sucht Text.
const klein = wort.toLowerCase().replace(/[\\%_]/g, (z) => `\\${z}`);
return [`${klein}%`, `% ${klein}%`];
});
}