Commit Graph
169 Commits
Author SHA1 Message Date
bjoernpoettkerandClaude Opus 5.5 33457c19c2 chore(docker): Node.js 20 → 24 LTS
Build and Push Multi-Platform Images / build-and-push (push) Successful in 1m22s
Node 20 ist seit April 2026 End-of-Life. Backend- und Frontend-Images
laufen jetzt auf node:24-alpine; @types/node im Backend auf ^24 angehoben.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 10:00:57 +01:00
bjoernpoettkerandClaude Opus 5.5 cdcee29c5b fix(agrarmonitor): Connector-Update – Lieferscheinnummer bei gebuchten Rechnungen
Build and Push Multi-Platform Images / build-and-push (push) Successful in 1m4s
Connector e6ce1d2 schickt die disabled-Felder aus editForm.php mit an
update.php. Bisher schlug „Lieferscheinnummer setzen“ bei gebuchten
Rechnungen mit HTTP 422 fehl (typ, firma_id, Adressfelder fehlten).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 15:55:36 +02:00
bjoernpoettkerandClaude Opus 5.5 0ff78707d1 feat(agrarmonitor): Cookies in den Einstellungen zurücksetzen
Build and Push Multi-Platform Images / build-and-push (push) Successful in 42s
Neuer Button „Cookies zurücksetzen“ neben „Status abrufen“. Er löscht
die gespeicherte Agrarmonitor-Session samt Cookie-Datei; beim nächsten
Zugriff meldet sich der Manager neu an.

Eine laufende Connector-Instanz wird über clearSession() zurückgesetzt
und verworfen, damit sie ihren alten Cookie-Jar nicht wieder in die
Datei schreibt. Während einer Lieferantennummern-Vergabe ist das
Zurücksetzen gesperrt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 09:53:47 +02:00
bjoernpoettkerandClaude Opus 5.5 549948913a fix(agrarmonitor): Lieferanten-Detailseite ohne AJAX-Header laden
Build and Push Multi-Platform Images / build-and-push (push) Successful in 38s
Im Einzeltest fehlte auf /kunden/detail/<id> das Feld
#lieferantennummer, obwohl die Liste kurz vorher mit derselben Session
geladen wurde. Die Detailseite wird jetzt wie eine normale
Browser-Navigation ohne X-Requested-With geladen (wie im Connector
bei den Eingangsrechnungen).

Fehlt das Feld trotzdem, nennt der Fehler HTTP-Status, Ziel-URL,
Seitentitel und Feldnamen – ohne Feldwerte.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 09:34:49 +02:00
bjoernpoettkerandClaude Opus 5.5 3d4125b29a feat(agrarmonitor): fehlende Lieferantennummern vergeben
Build and Push Multi-Platform Images / build-and-push (push) Successful in 1m1s
Neuer Bereich „Lieferantennummern überprüfen“ in den Agrarmonitor-
Einstellungen. Er vergibt allen aktiven Lieferanten ohne
Lieferantennummer eine fortlaufende Nummer im Schema 1555xxxx, ab der
höchsten vergebenen Nummer (Inaktive zählen mit, Altlasten werden
ignoriert).

- Vorschau „Lieferant → neue Nummer“, geschrieben wird erst nach
  Bestätigung; Einzelmodus zum Testen an einem Lieferanten
- formData wird wie in der Weboberfläche aus #form_detail aufgebaut,
  nur die Lieferantennummer ändert sich
- Dublettencheck über vorhanden.php, Erfolgskontrolle durch Neuladen
  des Details, Abbruch beim ersten Fehler
- Lauf im Hintergrund mit Status-Abfrage (wegen Proxy-Timeouts),
  Listenfilter werden danach zurückgesetzt
- HTML-Parsing mit cheerio, Unit-Tests für Parser und Vergabe-Ablauf

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 08:51:13 +02:00
bjoernpoettkerandClaude Opus 5.5 029009182a fix(agrarmonitor): Fehlergrund beim Speichern in Agrarmonitor loggen
Build and Push Multi-Platform Images / build-and-push (push) Successful in 50s
Connector-Update: Bei HTTP 422 von update.php steht jetzt die
Begründung von Agrarmonitor in der Warnung „Lieferscheinnummer setzen
fehlgeschlagen“.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 15:22:28 +02:00
bjoernpoettkerandClaude Opus 5.5 79ab3da2c2 fix(agrarmonitor): Connector mit neuen Eingangsrechnungs-Endpunkten
Build and Push Multi-Platform Images / build-and-push (push) Failing after 19s
Agrarmonitor hat eingangsrechnungen.php entfernt, dadurch schlug die
Livesearch im Upload-Check mit 404 fehl. Der Connector nutzt jetzt
editForm.php zum Laden und update.php zum Speichern.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 14:18:40 +02:00
bjoernpoettkerandClaude Opus 5.5 d2236368be fix(agrarmonitor): aufgerufene Seite bei HTTP-Fehlern mitloggen
Build and Push Multi-Platform Images / build-and-push (push) Successful in 43s
Fehlermeldungen im Polling enthielten nur den Status (z. B. „Request
failed with status code 404“), nicht aber die aufgerufene URL. Die neue
Hilfsfunktion fehlerText() hängt bei Axios-Fehlern Methode und
vollständige URL an, damit sich z. B. der Livesearch-404 einer
konkreten Seite zuordnen lässt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 14:02:59 +02:00
bjoernpoettkerandClaude Opus 5.5 a72e41611d feat(mail): E-Mail ignorieren, wenn im Import-Wizard alles ignoriert wird
Build and Push Multi-Platform Images / build-and-push (push) Successful in 23s
Stehen im Wizard alle Dokumente auf „Ignorieren“ (durch die
Duplikatprüfung oder vom Benutzer gewählt), gibt es nichts zu
importieren. „Weiter“ markiert die E-Mail dann als ignoriert, informiert
den Benutzer und schließt den Wizard.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 14:04:23 +02:00
bjoernpoettkerandClaude Opus 5.5 acb9076944 fix(mail): „Anhänge prüfen“ erkennt Duplikate auch über den Inhalt
Build and Push Multi-Platform Images / build-and-push (push) Successful in 32s
check-attachments fragte nur die Checksumme bei Paperless ab. Neu
erzeugte PDFs mit gleichem Inhalt wie ein bereits importierter Anhang
blieben dadurch auf „Neu“. Findet Paperless die Checksumme nicht, wird
jetzt per Text-Fingerprint nach einem importierten Anhang gesucht; die
Mail wird dann als verarbeitet markiert und die Paperless-ID übernommen.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 12:42:02 +02:00
bjoernpoettkerandClaude Opus 5.5 797cbb9b71 fix(mail): Duplikate auch bei neu erzeugten PDFs erkennen
Build and Push Multi-Platform Images / build-and-push (push) Successful in 45s
Absender erzeugen PDFs beim erneuten Versand oft neu (andere
Erstellungszeit/Dokument-ID), dadurch unterscheidet sich die Checksumme
und die Duplikatprüfung griff nicht (z.B. Mail 7071 vs. 7072).

- Neuer Text-Fingerprint (SHA-256 über normalisierten PDF-Text via
  pdf-parse) in Attachments.ContentFingerprint, inkl. Migration und Index
- Wird beim Mail-Abruf berechnet; Altbestand füllt ein Hintergrund-
  Backfill beim Start auf, fehlende Werte werden bei Bedarf nachberechnet
- Duplikatprüfung und Import-Sperre berücksichtigen Anhänge mit gleichem
  Inhalt, die importiert sind oder gerade importiert werden
- Jest braucht für pdf-parse --experimental-vm-modules (test-Skripte)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 12:27:31 +02:00
bjoernpoettkerandClaude Opus 5.5 534b313054 fix(mail): Duplikate auch bei gerade laufenden Importen erkennen
Build and Push Multi-Platform Images / build-and-push (push) Successful in 39s
Wurde dieselbe Mail zweimal weitergeleitet, erkannte der Import-Assistent
das Duplikat nicht, solange Paperless das erste Dokument noch verarbeitete –
geprüft wurde nur die Checksumme in Paperless.

- checkDuplicate() berücksichtigt zusätzlich offene Paperless-Tasks von
  Anhängen mit gleicher Checksumme (gleicher Seitenbereich)
- Assistent prüft erneut vor dem Abrufen der Belegnummern und vor dem
  Import; Duplikate werden ignoriert, reservierte Nummern freigegeben
- executeImport prüft serverseitig vor Upload und Festlegen der Nummer,
  sperrt laufende Importe gegen parallele Doppelimporte und meldet 409

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 10:29:43 +02:00
bjoernpoettkerandClaude Opus 5.5 3cea3fb239 feat(mail): E-Mails mit Leserecht als ignoriert markieren
Der Button „Als ignoriert markieren“ war für alle mit VIEW_MAIL sichtbar,
der Aufruf (PATCH :id/status) verlangte aber MANAGE_ALL und scheiterte
mit 403. Neuer Endpunkt POST /api/emails/:id/ignore mit VIEW_MAIL, der nur
Status 3 setzen kann; bereits verarbeitete E-Mails werden abgelehnt. Das
allgemeine Statusändern bleibt Admins vorbehalten.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 10:29:43 +02:00
bjoernpoettkerandClaude Opus 5 f97177943d feat(mail): abgeschlossene E-Mails erneut zur Bearbeitung freigeben
Build and Push Multi-Platform Images / build-and-push (push) Successful in 39s
Der Import-Wizard ist für verarbeitete E-Mails gesperrt. Damit ließ sich
ein fehlerhaft abgeschlossener Vorgang bisher nicht korrigieren.

Administratoren können eine E-Mail in der Detailansicht nun wieder
freigeben (POST /api/emails/:id/reimport, MANAGE_ALL). Die Anhänge aus
der Datenbank werden weiterverwendet; ein erneuter Abruf vom IMAP-Server
findet nicht statt.

Die Freigabe setzt Status 4 ("Zur Nachbearbeitung") statt Status 0:
check-attachments prüft Mails mit Status 0, findet die Anhänge per
Checksumme in Paperless – dort liegen sie ja bereits – und würde die
Freigabe beim nächsten Lauf sofort wieder auf "Verarbeitet" zurückdrehen.
Status 4 bleibt davon unberührt. Da Status ein freies int ist, ist dafür
keine Migration nötig.

Erlaubt sind nur die Status 1, 2 und 3; liegt die E-Mail ohnehin im
Arbeitsvorrat, antwortet der Endpunkt mit 400, statt den Zustand still zu
überschreiben. Wer die Freigabe ausgelöst hat, steht im Log.

Der neue Status erscheint als eigener Tag sowie in beiden Filtern der
Mailpostfach-Übersicht, damit die freigegebenen Mails auffindbar bleiben.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 11:06:08 +02:00
bjoernpoettkerandClaude Opus 5 b7b65c64ae fix: Berechtigungen der Tagesübersicht, WebDAV-Ausbau, Tests instand gesetzt
Build and Push Multi-Platform Images / build-and-push (push) Successful in 55s
Die tägliche E-Mail-Zusammenfassung zeigte Nutzern Bereiche, für die sie
keine Berechtigung haben. Zwei unabhängige Ursachen:

- Das Backend prüfte weiterhin die Altgruppe PM_Belege. Die Umbenennung
  zu PM_Buchhaltung (ca1d371) war nur im Frontend angekommen, weshalb
  der Digest "Manuell bearbeiten" und "In Agrarmonitor" anbot, während
  die Oberfläche beide Bereiche sperrte.
- Der Cron-Versand wertet die Gruppen aus user_settings aus. Diese Spalte
  wurde nur beim Aufruf der Benutzereinstellungen gefüllt; entzogene
  Berechtigungen erreichten den Digest daher unter Umständen nie.

Behoben durch Angleichen des Gruppen-Mappings und den neuen
UserIdentitySyncService, der E-Mail, Benutzername und Gruppen bei jedem
authentifizierten Request aus dem Token spiegelt – ohne den Request zu
blockieren und ohne DB-Zugriff, solange sich das Token nicht ändert. Die
doppelte Identitätspflege im UserSettingsService entfällt.

WebDAV wird nicht eingesetzt und ist entfernt; Export-Ziele bieten nur
noch FTP. Damit verschwindet das ESM-Paket webdav, an dem zwei
Jest-Suites bereits beim Parsen scheiterten.

Veraltete Tests instand gesetzt: email.controller und settings.controller
mockten weniger Abhängigkeiten, als die Klassen inzwischen haben;
postprocessing.service.spec beschrieb noch das alte Regelmodell mit
Einzelfeldern statt FilterJson und ist gegen die heutige Filter-Engine
neu geschrieben (AND/OR, verschachtelte Gruppen, Fehlerprotokollierung).

Enthält außerdem den Arbeitsstand der E-Rechnungs-Mandantenzuordnung, da
sich beide Änderungen dieselben Dateien teilen (SettingsPage, package.json).

98 Tests in 12 Suites grün, Backend- und Frontend-Build sauber.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 00:06:50 +02:00
bjoernpoettkerandClaude Fable 5 32ba772e4e fix(frontend): weiße Seite bei fehlgeschlagenem Chunk-Import beheben
Build and Push Multi-Platform Images / build-and-push (push) Successful in 18s
Alle Seiten werden per React.lazy als eigene Chunks nachgeladen. Schlug ein
Import fehl (Safari: "Importing a module script failed" — z.B. nach einem
Deployment mit neuen Chunk-Namen oder bei beschädigtem, wegen
"Cache-Control: immutable" nie revalidiertem Browser-Cache), riss der Fehler
mangels ErrorBoundary den kompletten React-Baum in eine weiße Seite.

- main.tsx: Bei 'vite:preloadError' automatisch neu laden (max. 1×/Minute,
  sessionStorage-Guard gegen Reload-Schleifen)
- AppErrorBoundary (neu): Fehlermeldung mit "Seite neu laden"-Button statt
  weißer Seite; bei Chunk-Ladefehlern mit Hinweis auf Cache-Leeren
- App.tsx: ErrorBoundary um alle Routen gelegt

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 10:20:25 +02:00
bjoernpoettkerandClaude Opus 4.8 095cc4bb02 fix: Warteschlange blieb stehen – Altersvergleich in die DB verlagert
Build and Push Multi-Platform Images / build-and-push (push) Successful in 32s
Der Mindest-Wartezeit-Check verglich Date.now() (Node-Uhr) mit dem aus der DB
gelesenen createdAt. Ohne gesetzte timezone (mysql2-Default 'local') wird
createdAt bei abweichender DB-Zeitzone als zukünftig interpretiert → Alter
negativ → jeder Eintrag galt als "zu jung" und wurde nie verarbeitet.

Der Vergleich läuft jetzt in der DB (createdAt <= NOW(6) - INTERVAL 5s via
TypeORM Raw), unabhängig von Zeitzone/Uhr des Node-Prozesses. Bereits
wartende Einträge werden dadurch nach dem Deploy sofort abgearbeitet.
Test-Mock bildet den DB-Alters-Filter nach.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 12:07:51 +02:00
bjoernpoettkerandClaude Sonnet 4.6 7b2a79be2a fix: Dateinamen mit Pfadtrennzeichen beim Mail-Import absichern
Build and Push Multi-Platform Images / build-and-push (push) Successful in 31s
Anhänge mit '/' im Dateinamen (z.B. "Rechnung - 2026/002650322.PDF")
führten zu einem Fehler, weil '/' als Verzeichnistrenner interpretiert
wurde und der temp. Pfad nicht existierte.

- sanitizeFilename() ersetzt '/' und '\' durch '-', entfernt '..' und Null-Bytes
- Wird beim Speichern in die DB (email-download) und beim Aufbau des
  temp. Pfads (email-import) angewendet (defense-in-depth)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-22 11:10:36 +02:00
bjoernpoettkerandClaude Opus 4.8 1c70473cef feat: Mindest-Wartezeit (5000 ms) zwischen Einreihen und Verarbeitung
Build and Push Multi-Platform Images / build-and-push (push) Successful in 33s
processQueue verarbeitet den ältesten Warteschlangen-Eintrag erst, wenn er
mindestens MIN_QUEUE_AGE_MS (Default 5000 ms, ENV WEBHOOK_QUEUE_MIN_AGE_MS)
in der Tabelle lag. Ist der älteste Eintrag (FIFO) noch zu jung, bricht der
Tick ab und prüft beim nächsten Intervall erneut. Unit-Test ergänzt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 11:03:10 +02:00
bjoernpoettkerandClaude Fable 5 beaa1be4a5 feat(backend): Metadaten direkt beim Paperless-Upload übergeben
Build and Push Multi-Platform Images / build-and-push (push) Successful in 31s
E-Mail-Import und External-Upload setzen ASN, Custom Fields (CF3/CF7/CF9),
Dokumenttyp, Tags, Owner und Belegdatum jetzt bereits beim post_document,
statt erst nachträglich über den Task-Processor-Cron (30 s + PATCH).

- Neu: deriveTaskMetadata() in task-metadata.util.ts als gemeinsame
  Ableitung (Task-Felder → Paperless-Metadaten) für beide Upload-Pfade
  und den Task-Processor – identische Parsing-Regeln, kein Copy-Paste
- Task-Processor nutzt den Helper (verhaltensidentischer Refactor) und
  patcht weiterhin idempotent nach; zuständig bleibt er für alles
  ID-Abhängige (CF8-Verknüpfungen, Anlage-Titel, Owner-Entfernung, Notizen)
- FAILURE-Zweig loggt Paperless-Fehlertext und Belegnummer vor dem
  Löschen des Tasks (ASN-Kollision kann jetzt den Consume scheitern lassen)
- Tests: 18 Unit-Tests für deriveTaskMetadata, 3 Charakterisierungstests
  pinnen den PATCH-Payload von processSuccessfulTask

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 22:46:42 +02:00
bjoernpoettkerandClaude Opus 4.8 57c8964384 feat(frontend): Manuell bearbeiten – Speichern trotz fehlender Pflichtfelder
Build and Push Multi-Platform Images / build-and-push (push) Successful in 17s
Im "Manuell bearbeiten"-Dialog (DocumentEditModal, isPosteingang=false) kann
jetzt trotz leerer Pflichtfelder gespeichert werden: Schlägt die Formular-
Validierung fehl, erscheint ein Bestätigungsdialog, der die fehlenden Pflicht-
felder auflistet; per "Trotzdem speichern" wird ohne erneute Validierung
gespeichert. Der Posteingang (isPosteingang=true) blockiert wie bisher.

Die Kontonummer-Logik wurde in runSave() ausgelagert und von beiden Pfaden
(normal + "trotzdem speichern") wiederverwendet.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 10:55:04 +02:00
bjoernpoettker 8390d03869 Merge pull request 'Freigabe → main: Mobile UI, Webhook, IMAP, Zahlung-Workflow und weitere Features' (#5) from Freigabe into main
Build and Push Multi-Platform Images / build-and-push (push) Successful in 8s
Reviewed-on: #5
2026-07-14 07:44:32 +00:00
bjoernpoettker 3cbb64686a chore: add .gitea_token to .gitignore
Build and Push Multi-Platform Images / build-and-push (push) Successful in 10s
2026-07-14 09:43:53 +02:00
bjoernpoettkerandClaude Sonnet 4.6 7076eef57b feat: add mobile-responsive layout and card views across all pages
Build and Push Multi-Platform Images / build-and-push (push) Successful in 39s
- New useIsMobile hook and MobileCardList component
- AppLayout: hamburger menu + Drawer navigation on mobile
- All list pages (Inbox, Posteingang, Manuell, Mail, Freigabe, Zahlung, TaskLog)
  show card layout on mobile instead of tables
- CSS: responsive modal height, horizontal table scroll, text-size-adjust
- Backend: Agrarmonitor polling fixes, Zahlung service improvements,
  IMAP folder service extended, misc controller fixes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 09:22:58 +02:00
bjoernpoettkerandClaude Opus 4.8 0765d14d3b fix(agrarmonitor): Karenzzeit vor "Von AM zurück"-Markierung
Build and Push Multi-Platform Images / build-and-push (push) Successful in 39s
Belege, die per E-Mail an Agrarmonitor gesendet werden, brauchen bis zu
10 Minuten bis zum Import in den Dateieingang. Bisher wurden sie im
Upload-Check sofort als "Manuell bearbeiten" + Tag 19 markiert, wenn sie
weder verbucht noch im Dateieingang waren – auch mitten im Import-Fenster.

Jetzt wird vor dem Markieren geprüft, ob die jüngste Notiz mit dem
konfigurierbaren Marker-Text jünger als die Wartezeit ist; falls ja, wird
der Beleg übersprungen und beim nächsten Lauf erneut geprüft. Wartezeit
(agrarmonitor_import_wartezeit_minuten, Standard 10) und Marker
(agrarmonitor_notiz_marker, Standard "Agrarmonitor") sind als Settings
konfigurierbar und in der Settings-Seite editierbar.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 12:40:59 +02:00
bjoernpoettkerandClaude Opus 4.8 8fda248683 fix(frontend): seltener Relogin durch stillen Token-Refresh
Build and Push Multi-Platform Images / build-and-push (push) Successful in 17s
Zwei Lücken behoben, die bei abgelaufenem Access-Token (v.a. nach inaktivem
Tab, dessen Renew-Timer gedrosselt wurde) zu vollständigen Neuanmeldungen
führten:

- getAccessToken erneuert das Token jetzt proaktiv (signinSilent), wenn es
  abgelaufen ist, statt ein abgelaufenes Token zu senden. Profitiert auch den
  direkten fetch-Aufrufen (Bilder/PDFs).
- Der 401-Interceptor versucht zuerst einen stillen Refresh und wiederholt die
  Anfrage einmal; erst wenn das scheitert (Refresh-Token ungültig), erfolgt der
  Login-Redirect.

Gleichzeitige Refreshes werden dedupliziert (renewToken), um invalid_grant bei
Refresh-Token-Rotation zu vermeiden.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 11:53:38 +02:00
bjoernpoettkerandClaude Opus 4.8 156b0401b3 fix(auth): JWT nicht als API-Key prüfen (Bearer-Fallback nur für pm_-Keys)
Build and Push Multi-Platform Images / build-and-push (push) Successful in 30s
Beim JWT-Ablauf fällt der JwtOrApiKeyGuard auf den ApiKeyGuard zurück. Dieser
nahm bisher jedes Authorization-Bearer-Token als API-Key-Kandidaten – also auch
das (abgelaufene) JWT – und loggte eine irreführende "Invalid API Key"-Warnung
(samt JWT-Präfix). Der Bearer-Fallback akzeptiert nun nur noch Token mit dem
API-Key-Präfix "pm_"; ein JWT wird ignoriert. Ergebnis bleibt 401 (Frontend
re-authentifiziert), aber ohne irreführenden Log.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 11:12:53 +02:00
bjoernpoettkerandClaude Opus 4.8 e50111a731 fix(frontend): einheitliche Schriftart in AntD-Komponenten
Build and Push Multi-Platform Images / build-and-push (push) Successful in 17s
fontFamily-Token im ConfigProvider auf denselben Inter-Stack wie der Body
gesetzt. Zuvor fehlte das Token, sodass AntD-Eingaben (z.B. das Select-
Suchfeld) im AntD-Default-Stack abwichen, während der angezeigte Wert die
Body-Schrift (Inter) nutzte.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 11:09:52 +02:00
bjoernpoettkerandClaude Opus 4.8 9718d6888a feat: Webhook-Warteschlange in Datenbank persistieren
Build and Push Multi-Platform Images / build-and-push (push) Successful in 31s
Die Warteschlange liegt nicht mehr im Speicher, sondern in der neuen Tabelle
webhook_queue (Entity WebhookQueueItem, documentId als Primärschlüssel). So
überstehen ausstehende IDs einen Neustart und werden nach dem Boot
weiterverarbeitet.

- Neue Entity + Migration (CreateWebhookQueue); in data-source.ts/Barrel
  registriert. Dev: synchronize legt die Tabelle an; Prod: migrationsRun.
- enqueue nutzt INSERT IGNORE (atomar, race-sicher) -> Dedup über den PK.
- processQueue holt FIFO (createdAt ASC), entfernt die Zeile vor der
  Verarbeitung, arbeitet sequenziell (isProcessing-Guard).
- getStatus/queueSize lesen die DB-Anzahl. Controller awaitet enqueue.
- Unit-Test mit simuliertem FIFO-Repo: Dedup, Remove-on-start, Re-Enqueue,
  No-Parallel (deterministisch).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 17:54:16 +02:00
bjoernpoettkerandClaude Opus 4.8 66a2cccd20 feat: Webhook-Warteschlange für sequenzielle, deduplizierte Verarbeitung
Build and Push Multi-Platform Images / build-and-push (push) Successful in 37s
Der Webhook reiht eingehende Dokument-IDs nur noch in eine Warteschlange ein
und antwortet sofort (status "queued"). Ein separater Intervall-Prozess
(WebhookQueueService) arbeitet die IDs nacheinander ab – ohne Überschneidung,
falls ein Dokument mehrfach kurz hintereinander gespeichert wird:

- Jede ID kommt nur einmal in der Liste vor (Dedup).
- Beim Verarbeitungsstart wird die ID sofort entfernt; ein erneutes Feuern
  während der Verarbeitung reiht sie wieder ein (ein weiterer Lauf folgt).
- Es läuft immer nur eine Verarbeitung gleichzeitig (isProcessing-Guard).
- Prüfintervall sehr kurz (WEBHOOK_QUEUE_INTERVAL_MS, Default 1000 ms).

Status-Recording (last_webhook_call) + GET /api/webhook/status wandern in den
Queue-Service und liefern zusätzlich die aktuelle Warteschlangen-Größe.
Frontend-Tab zeigt Status "In Warteschlange" und die Queue-Größe an.
Unit-Test deckt Dedup, Remove-on-start, Re-Enqueue und No-Parallel ab.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 17:09:23 +02:00
bjoernpoettkerandClaude Opus 4.8 b77a283ab2 feat: Webhook verarbeitet jedes Dokument ohne Tag-16-Vorprüfung
Build and Push Multi-Platform Images / build-and-push (push) Successful in 38s
processDocumentById verarbeitet nun jedes vom Paperless-Webhook gemeldete
Dokument, unabhängig vom Tag "paperlessmanager" (ID 16). Die Tag-Vorprüfung
(skipped/tag-missing) entfällt. Die Konstante PAPERLESSMANAGER_TAG_ID bleibt
für den manuellen Batch-Lauf processDocuments erhalten.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 16:58:34 +02:00
bjoernpoettkerandClaude Opus 4.8 1c37936d8c feat(frontend): Webhook-Status-Tab in den Einstellungen
Neuer Tab "Webhook" in der SettingsPage zeigt den letzten Paperless-
Webhook-Aufruf (Zeitpunkt, Dokument-ID, Aktion, Ergebnis) über
GET /api/webhook/status an. Neue API-Datei src/api/webhook.ts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 16:01:27 +02:00
bjoernpoettkerandClaude Opus 4.8 32d9e84985 feat: letzten Paperless-Webhook-Aufruf in Settings festhalten
Pro Webhook-Aufruf wird ein Status in der settings-Tabelle gepflegt
(Tag "last_webhook_call", JSON mit Zeitpunkt, Dokument, action, Ergebnis).
Neuer Endpunkt GET /api/webhook/status liefert den letzten Aufruf zum
Auslesen (JWT oder API-Key). Persistent über Neustarts, anders als die
Container-Logs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 15:57:36 +02:00
bjoernpoettkerandClaude Opus 4.8 56596d4482 feat: Paperless-Webhook ersetzt Tag-16-Cron-Job durch Echtzeit-Verarbeitung
Build and Push Multi-Platform Images / build-and-push (push) Successful in 42s
Statt stündlichem Cron ruft Paperless-NGX nun per Webhook das Backend auf,
sobald ein Dokument bearbeitet wurde. Der Endpunkt verarbeitet ein einzelnes
Dokument (per doc_url/document_id), prüft weiterhin den Tag "paperlessmanager"
(ID 16) und ist per API-Key (X-API-Key) abgesichert, sodass nur Paperless ihn
aufrufen kann.

- paperless-processor.service.ts: @Cron entfernt; neue Methode
  processDocumentById; Tag-16 als Konstante; gemeinsamer Helper
  processAndEvaluate (Batch + Webhook)
- paperless.module.ts: PaperlessProcessorService exportiert
- webhook.controller.ts: Route auf api/webhook (erreichbar via /api-Proxy);
  @Public() -> @UseGuards(ApiKeyGuard); ID-Extraktion aus {{doc_url}}
- webhook.module.ts: PaperlessModule + AuthModule importiert

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 15:20:19 +02:00
bjoernpoettkerandClaude Sonnet 4.6 4d05a94681 feat: implement two-step Freigabe→Zahlung workflow
Adds a payment step after document approval: PM_Freigabe approves
(Field 15 = "freigegeben"), then PM_Zahlung can mark as paid (Field 16).

- Backend: VIEW_ZAHLUNG permission mapped to PM_Zahlung OIDC group
- Backend: ZahlungModule with endpoints to list documents by filter
  (ausstehend/freigegeben/alle), set Field 16, fetch options from Paperless
- Backend: setZahlung() throws ForbiddenException if Field 15 ≠ "freigegeben"
- Frontend: /zahlung route with 3-way filter, two status columns (Freigabe + Zahlung)
- Frontend: "Zahlung verbuchen" button disabled with tooltip for non-approved docs
- Frontend: Zahlung menu item with EuroOutlined icon

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 21:29:50 +02:00
bjoernpoettkerandClaude Sonnet 4.6 969f0ae0b1 feat: verarbeitete/ignorierte E-Mails beim Prüfen in IMAP-Ordner verschieben
Build and Push Multi-Platform Images / build-and-push (push) Successful in 38s
- Cleanup-Cron von EmailDownloadService in ImapFolderService verschoben,
  damit er auch aus EmailController aufrufbar ist (zirkuläre Abhängigkeit vermieden)
- Beim Klick auf „Anhänge prüfen" wird der IMAP-Cleanup fire-and-forget gestartet
- Beim Klick auf „Bereits verarbeitete Anhänge prüfen" werden zusätzlich alle
  E-Mails im IMAP-Posteingang, die in der DB als verarbeitet (Status 1) oder
  ignoriert (Status 3) markiert sind, in den Ordner „importiert" verschoben
- Erfolgsmeldung zeigt Anzahl verschobener E-Mails an

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 13:04:03 +02:00
bjoernpoettkerandClaude Opus 4.8 41eed1871e fix: Produktions-Crash durch TypeORM-synchronize beheben
Build and Push Multi-Platform Images / build-and-push (push) Successful in 44s
NODE_ENV=production deaktiviert synchronize (zerstörerischer ADD/DROP-COLUMN-
Churn auf MariaDB, der die 8126-Byte-Zeilengröße sprengte) und aktiviert
migrationsRun. Neue data-source.ts als einzige Konfigquelle (Laufzeit + CLI),
Migrations-Workflow (generate/run/revert) inkl. dotenv ergänzt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 09:27:04 +02:00
bjoernpoettkerandClaude Sonnet 4.6 ed57477324 feat: importierte E-Mails automatisch in IMAP-Ordner verschieben und nach 90 Tagen löschen
Build and Push Multi-Platform Images / build-and-push (push) Successful in 31s
- Neuer ImapFolderService verschiebt E-Mails nach erfolgreichem Import in den
  konfigurierbaren Ordner "importiert" (wird bei Bedarf automatisch erstellt)
- Täglicher Cron um 03:00 Uhr verschiebt E-Mails älter als 90 Tage in den
  Papierkorb und leert ihn anschließend
- createImapClient()-Hilfsmethode im EmailDownloadService ausgelagert
- IMAP_IMPORTED_FOLDER und IMAP_TRASH_FOLDER in docker-compose ergänzt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 21:58:41 +02:00
bjoernpoettker c665451abf Merge pull request 'Freigabe' (#4) from Freigabe into main
Build and Push Multi-Platform Images / build-and-push (push) Successful in 9s
Reviewed-on: #4
2026-06-16 14:49:23 +00:00
bjoernpoettkerandClaude Opus 4.8 ef7813f9f9 ci: add manual build workflow with custom image tag
Build and Push Multi-Platform Images / build-and-push (push) Successful in 11s
New workflow_dispatch workflow to build & push backend/frontend images
with a manually chosen tag and service selection (both/backend/frontend).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 16:48:04 +02:00
bjoernpoettker 66aeab282c Revert "fix: resolve all ESLint errors in backend and frontend"
Build and Push Multi-Platform Images / build-and-push (push) Successful in 19s
This reverts commit 07dfd7e840.
2026-06-16 16:19:11 +02:00
bjoernpoettker 14c11bf718 Revert "feat: auto-move imported emails to IMAP folder and add 90-day cleanup"
This reverts commit b1b30fe1dd.
2026-06-16 16:19:11 +02:00
bjoernpoettkerandClaude Sonnet 4.6 b1b30fe1dd feat: auto-move imported emails to IMAP folder and add 90-day cleanup
Build and Push Multi-Platform Images / build-and-push (push) Successful in 41s
- New ImapFolderService moves emails to configurable "importiert" folder
  after successful import, creating the folder if it doesn't exist
- Daily cron at 03:00 moves emails older than 90 days to trash and empties it
- Extract createImapClient() helper in EmailDownloadService
- Add ensurePageCache() with in-flight deduplication to BarcodeScannerService
- InboxService regenerates page cache on-demand when image file is missing
- IMAP_IMPORTED_FOLDER and IMAP_TRASH_FOLDER added to .env.example and docker-compose

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 13:53:56 +02:00
bjoernpoettkerandClaude Opus 4.8 07dfd7e840 fix: resolve all ESLint errors in backend and frontend
Backend 958→0 errors, frontend 98→0 errors. Builds and tsc clean.

Echte Fixes:
- Auth: AuthenticatedUser/AuthenticatedRequest, JwtStrategy + alle 5
  Controller von `@Request() req: any` auf typisierten Request umgestellt
- Error-Handling: neuer getErrorMessage/Stack/Code/getResponseData-Helper;
  alle 50 `catch (err: any)`-Blöcke auf `unknown` + Helper umgestellt
- 24 echte Bugs: require-await, require-imports→ES-Imports, useless-escape,
  misused-promises, tote Imports/Vars, leere catch-Blöcke kommentiert
- document-pipeline: OCR-Ergebnis wird nicht gespeichert (als TODO markiert)

Pragmatisch auf warn herabgestuft (untypisierte Paperless-NGX-API):
no-unsafe-*, restrict-template-expressions, no-base-to-string,
no-explicit-any (FE), react-refresh/only-export-components

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 21:33:37 +02:00
bjoernpoettkerandClaude Sonnet 4.6 d96e06e86d feat: add Steuertags concept to separate workflow from content tags
Build and Push Multi-Platform Images / build-and-push (push) Successful in 38s
- New steuertag_ids setting to mark tags as workflow-only (not editable)
- DocumentEditModal shows only content tags (non-Steuertags) as editable chips
- Backend preserves Steuertags when saving document tag changes
- ManuellBearbeitenPage renders content tag chips under document title
- New Steuertags settings tab with multi-select and color preview

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 11:46:39 +02:00
bjoernpoettkerandClaude Sonnet 4.6 dad0136365 chore: apply ESLint auto-fix across entire backend
Build and Push Multi-Platform Images / build-and-push (push) Successful in 41s
Reformats code style (line breaks, indentation, type annotations)
without changing logic. Also includes minor feature additions bundled
in the same lint run (stats service, user-settings groups, agrarmonitor
polling improvements).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 09:02:02 +02:00
bjoernpoettkerandClaude Sonnet 4.6 4c75a1ded2 feat: filter digest tiles by user permissions and add import progress status
Build and Push Multi-Platform Images / build-and-push (push) Successful in 42s
- Store UserGroups from OIDC in UserSettings entity, sync on each request
- Filter daily digest tiles based on user's permission groups
- Add in-memory job status tracking to EmailImportService
- Poll import job status in MailImportWizard and show progress in Spin tip

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 16:29:56 +02:00
bjoernpoettkerandClaude Sonnet 4.6 2747b0046a feat: redesign daily digest email with card layout and timezone fix
Build and Push Multi-Platform Images / build-and-push (push) Successful in 33s
- Replace table layout with modern card-based design per dashboard area
- Add icon, color accent, badge and "Öffnen" link per card
- Show summary bar with total open items count
- Fix cron timezone to Europe/Berlin

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 11:00:20 +02:00
bjoernpoettkerandClaude Sonnet 4.6 15e06bd60f fix: strip trailing slashes from APP_URL and AGRARMONITOR_BASE_URL
Build and Push Multi-Platform Images / build-and-push (push) Successful in 54s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 20:34:35 +02:00
bjoernpoettkerandClaude Sonnet 4.6 184ac3f5cc feat: add clickable links to daily digest emails via APP_URL
Build and Push Multi-Platform Images / build-and-push (push) Successful in 34s
- Read APP_URL and AGRARMONITOR_BASE_URL from config
- Render dashboard entries as clickable links in HTML digest email
- Add APP_URL and DAILY_DIGEST_CRON to .env.example and docker-compose.yml

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 18:12:02 +02:00