Drei while ((m = re.exec(t)) !== null)-Schleifen (dkv-parser.service.ts,
dkv-parser.validate.ts, icon-discovery.service.ts) sind die korrekte
Standardform fuer globale Regexe - kein verrutschtes "=". Umgeschrieben auf
eine verhaltensgleiche for-Schleife, die ohne noAssignInExpressions-
Unterdrueckung auskommt: Zuweisung wandert in Initialisierung und
Fortschaltung der for-Schleife, Bedingung prueft weiterhin auf null.
Abfolge der exec-Aufrufe, lastIndex-Fortschritt und Rumpfinhalte
unveraendert. Neue Spezifikation dkv-parser.service.spec.ts deckt
parseDkvText erstmals eigenstaendig ab (zwei Fahrzeugbloecke, Rechnungsnummer
und -datum aus einer gemockten pdf-parse-Attrappe) - das Rueckfall-Tor fuer
diesen Umbau. dkv-parser.validate.ts bleibt bei 27 Fahrzeugbloecken/66
Transaktionen gegen die reale invoice.pdf identisch.
LdapService.escapeLdapFilterValue bleibt zeichengleich: der NUL-Treffer in
der Regel ist die von RFC 4515 vorgeschriebene \00-Maskierung, kein Fehler.
Ein biome-ignore-Kommentar dokumentiert das, statt die Funktion zu aendern.
locale-switcher.tsx setzt jetzt SameSite=Lax auf dem NEXT_LOCALE-Cookie -
path=/ und max-age waren bereits korrekt, es lag also kein Persistenzdefekt
vor. Ohne SameSite haengt die Uebertragung am Browservorgabewert statt an
einer Festlegung. Neue Spezifikation locale-switcher.test.tsx haelt die
vollstaendige geschriebene Cookie-Zeichenkette fest.
Biome-Warnungen 446 -> 434 (noControlCharactersInRegex/useIterableCallbackReturn/
noGlobalIsNan auf 0, noAssignInExpressions auf 4 und noDocumentCookie auf 17 -
beide Reste ausschliesslich in Testdateien, suppressions/unused auf 0).
Quick-Vorgang 260921-i8x, Task 3/3.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
- Aufgabe 2: vier sichere Biome-Regeln (useImportType pfadgebunden auf
apps/web+packages, noUselessEscapeInRegex, useConst,
useExponentiationOperator) sowie fuenf ungesicherte Regeln
(useNodejsImportProtocol, useLiteralKeys, useOptionalChain, useTemplate,
useParseIntRadix) angewendet und den gesamten Diff von Hand gelesen
(ldap.service.ts zeichenweise gegen Gross-/Kleinschreibung der
AD-Merkmale, auth.service.ts/jwt.strategy.ts gegen Durchwinken bei
fehlender Sitzung geprueft)
- noUselessSwitchCase bleibt bewusst stehen (tender-normalizer.service.ts:60,
die Fallmarke dokumentiert Absicht)
- Toter Code (D-03): fuenf folgenlose Auffangvariablen entfernt, eine
nicht benutzte Funktion (forSystemQuery, Pruefskript) entfernt, ein
positionsgebundener Dekoratorparameter umbenannt (current-user.decorator.ts),
fuenf Symptomfunde entfernt und als Folgeaufgaben zu melden (siehe unten)
- Sechs weitere, im Plan nicht namentlich gelistete aber
gleich-kategorische Dead-Code-Fundstellen in Testdateien zusaetzlich
bereinigt (groups.service.spec.ts, cert-manager.test.tsx,
ldap.service.spec.ts, prisma-tenant.extension.spec.ts x3) — noetig, um
die vom Plan selbst verlangten Nullstaende bei noUnusedVariables/
noUnusedImports/noUnusedFunctionParameters zu erreichen
Dekoratordaten aus apps/api unveraendert (593 Zeilen, sha256 6e1583f1...).
Endstand 620 Befunde (541 echt, 79 Test) statt der im Plan geschaetzten
621/542 — eine Differenz von 1, weil das Streichen des Namens aus
`catch (e: any)` in calendar.service.ts (Symptom-Fix) den dort ebenfalls
gemeldeten noExplicitAny-Befund miteliminiert; das ist eine erwuenschte
Nebenwirkung, keine Regression. Fehlerstufe 0, beide Testlaeufe
punktgleich gruen (69/1124, 66/459), pnpm type-check 4/4, pnpm lint
--force 5/5.
Folgeaufgaben aus D-03 (nicht in diesem Vorgang behoben):
- force-password-change.interceptor.ts: Freigabeliste prueft nur den Pfad,
nicht die HTTP-Methode
- change-password/page.tsx: nach erzwungenem Wechsel bleibt die Person auf
der Seite stehen (keine Weiterleitung, keine Aktualisierung der
Benutzerablage)
- VehicleTable.tsx: Loeschschaltflaeche hat keinen Besetztzustand, laesst
sich doppelt ausloesen
- SplitTab.tsx: downloadAllAsZip erhielt eine ungenutzte
Uebersetzungsfunktion, Hinweis auf fest verdrahtete Texte im Zip-Pfad
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
Date fix: previous regex matched payment-due date ("10 Tage nach Rechnungsdatum...
10.04.2026") instead of actual Rechnungsdatum. New approach anchors on the
invoice number line (DD/DDDDDDDDD/DDD) and takes the date on the next line,
which is always the actual Rechnungsdatum in DKV PDFs.
Exchange dedup: FindItem now filters IsRead=false (combined with sender filter
via <t:And>), so already-processed emails are skipped automatically.
After downloading attachments, UpdateItem marks the message as read
(using ItemId + ChangeKey from GetItem response), mirroring IMAP \Seen behavior.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Parser now extracts Rechnungsnummer (DD/DDDDDDDDD/DDD) and Rechnungsdatum
from PDF text, so filename doesn't rely on email subject
- Export filename changed from DKV_YYYY-MM_... to RG-DKV-{nr}-{YYMMDD}.xlsx
e.g. RG-DKV-26-650869002-002-260331.xlsx
- Subject fallback now also matches slash-separated invoice numbers (26/NNN/NNN)
- writeAndPrune simplified to accept baseName instead of separate fields
- Validation regex and prune prefix updated to match new RG-DKV- pattern
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Analyzed Invoice-4302486921-26_650869002_000.pdf text structure. Three cases:
1. FUEL (fields[4] = numeric tx-nr): km+product merged in fields[5], unit in
fields[6]. Already working; no change.
2. EV CHARGING (fields contains "DDDD KWH" or "DDDD MIN" unit): column layout
shifts — no km field, station+ort sometimes merged in fields[1]. Detected by
regex on unit field; kwhIdx drives relative offset for menge/netto/brutto.
Ort extracted from fields[2] (kwhIdx>=5) or fields[1] (kwhIdx=4, compact).
Kilometerstand = 0 (EV chargers don't record odometer).
3. SERVICE ROWS (e.g. "DKV Analytics Premiu"): appear inside a VEHICLE: block but
fields[4] is non-numeric (product description, not a transaction number). These
were being parsed as fake vehicle transactions producing wrong ort/km values.
Now filtered out (return null).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three issues fixed:
1. Kennzeichen normalization: DKV PDF extracts plates without hyphens
("GP JL 740E" vs CSV-imported "GP-JL 740E"). Added _normalizeKennzeichen()
which strips hyphens, spaces, and dots before lookup — resolves vehicle
master match failure that caused Marke/Modell/Fahrer to appear empty.
2. Empty-string NaN: parser used ?? '0' which doesn't catch empty strings,
causing parseDE('') = NaN. Changed to || '0' for km, menge, and totals.
3. Invalid km values: EV charging rows from DKV have misaligned columns —
km position contains a decimal price (e.g. 18.64 EUR or kWh). Added
sanity check: non-integer km values are written as null (empty cell)
instead of a misleading decimal. ExportRow.kilometerstand is now number|null.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- dkv-parser.validate.ts: empirical validation script against user-files/invoice.pdf
- 27 vehicle blocks, 66 transactions extracted (matches expected count)
- Handles two PDF extraction formats: single-tx (tab-separated) + multi-tx (columnar)
- German number parsing: replace(/\./g,'').replace(',','.') applied to km and menge
- Exits 1 with full raw text dump if zero vehicle blocks parsed (assertion guard)
- dkv-parser.service.ts: @Injectable() NestJS service wrapping validated logic
- parsePdf(buffer: Buffer): Promise<DkvVehicleBlock[]>
- Uses pdf-parse v2 class API: new PDFParse({data:buffer}) — NOT v1 pdfParse()
- Calls destroy() after extraction (T-07-01 memory safety)
- Generic error messages only on parse failure (T-07-02 info disclosure)
Regex adjustment vs Research Pattern 4: space-based regex replaced with
tab-split (single-tx) + columnar transpose (multi-tx) after empirical analysis
of actual invoice.pdf text extraction output.