Commit Graph
121 Commits
Author SHA1 Message Date
bjoernpoettkerandClaude Opus 5.5 8e0c609802 feat: Seiten- und Wortzahl für den Duplikatvergleich
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 13:55:51 +01:00
bjoernpoettkerandClaude Opus 5.5 57a460ffa2 feat(paperless): Belegnummer gegen Paperless prüfen (CF7 + ASN)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 13:53:50 +01:00
bjoernpoettkerandClaude Opus 5.5 bc2d1cd4b7 fix(agrarmonitor): gebuchte Belege wieder erkennen, Titel per Template
Build and Push Multi-Platform Images / build-and-push (push) Successful in 18s
- Connector auf 9bda65e: Buchungsstatus aus #bookedStatus lesen. AM zeigt
  hinter dem Status jetzt „Zahlung erfassen“, dadurch war buchungsDatum
  immer leer und das Polling hat keinen Beleg als verbucht markiert.
- Polling: Schritt-Logging pro Dokument über AGRARMONITOR_POLLING_DEBUG,
  Abschlusszeile zählt „noch nicht gebucht“ mit.
- Upload-Check: Titel über das Titel-Template des Dokumenttyps, Fallback
  auf ERG/EGU <Belegnr>. Template-Auflösung als resolveTitelTemplate()
  ausgelagert und im Processor wiederverwendet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 14:03:27 +01:00
bjoernpoettkerandClaude Opus 5.5 df50470e53 fix(paperless): documents.checksum für SHA-256 verbreitern
Build and Push Multi-Platform Images / build-and-push (push) Successful in 28s
Paperless-NGX ≥ 3.0 liefert original_checksum als SHA-256 (64 Zeichen).
Die lokale Spiegeltabelle documents hatte nur varchar(32), wodurch die
Nachbearbeitung mit „Data too long for column 'checksum'“ abbrach – nach
dem Setzen von Notizen, aber vor Fertig=1. Der Task wurde dadurch jede
Minute erneut verarbeitet und erzeugte doppelte Notizen.

- documents.checksum auf varchar(64) (Entity + Migration)
- Sync der Spiegeltabelle ist nicht mehr fatal: Fehler werden als
  Warnung geloggt, der Task wird trotzdem abgeschlossen

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 13:35:45 +01:00
bjoernpoettkerandClaude Opus 5.5 4f34855c12 fix(paperless): Anpassung an Paperless-NGX 3.4 (API v10)
Build and Push Multi-Platform Images / build-and-push (push) Successful in 31s
- Task-Status: getTask() normalisiert die paginierte v10-Antwort
  (status klein, result_data, related_document_ids). Bisher wurde jede
  Task als „nicht gefunden“ gelöscht, ohne Nachbearbeitung.
- Checksummen: Paperless ≥ 3.0 nutzt SHA-256 statt MD5. Duplikatprüfung
  umgestellt; Migration verbreitert Attachments.Checksum und berechnet
  Bestandswerte per SHA2() neu.
- Custom-Field-Suche über custom_field_query, der alte Filter wurde von
  Paperless ignoriert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 20:15:01 +01:00
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 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 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 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 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 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 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 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
bjoernpoettkerandClaude Sonnet 4.6 52438ee11f feat: add daily digest email notification module
Build and Push Multi-Platform Images / build-and-push (push) Successful in 50s
- New DailyDigestModule with scheduled summary email for open dashboard items
- Extract StatsService from StatsController for reuse in digest
- Add DailyDigestEnabled, UserEmail, UserPreferredUsername to UserSettings entity
- Sync email/username from OIDC token on each get/update call
- Add dailyDigestEnabled to UserSettingsDto and update API
- Notifications tab in UserSettingsPage with enable toggle and "Jetzt senden" button

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 15:57:10 +02:00
bjoernpoettkerandClaude Sonnet 4.6 029d5b351f fix: also set tag 19 (Von AM zurück) when marking document as manual
Build and Push Multi-Platform Images / build-and-push (push) Successful in 32s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 19:39:16 +02:00
bjoernpoettkerandClaude Sonnet 4.6 2444821c9e refactor: rename tagPosteingang to tagManuell for missing AM entries
Build and Push Multi-Platform Images / build-and-push (push) Successful in 34s
- Renamed setting agrarmonitor_tag_posteingang → agrarmonitor_tag_manuell
- Documents not found in AM are now tagged as "Manuell bearbeiten"
  instead of being moved back to Posteingang

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 22:17:01 +02:00
bjoernpoettkerandClaude Sonnet 4.6 55b30f1f39 feat: skip documents still in Agrarmonitor Dateieingang during upload check
Build and Push Multi-Platform Images / build-and-push (push) Successful in 31s
- Before moving a document back to Posteingang, check if it's still
  waiting in the Agrarmonitor Dateieingang
- If yes: skip silently (upload is pending processing)
- If no: move to Posteingang tag as before
- Handle 401/403 by clearing the session and aborting the check

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 22:13:34 +02:00
bjoernpoettkerandClaude Sonnet 4.6 e6436b2b9c feat: tag documents as Posteingang when AM entry is missing during upload check
Build and Push Multi-Platform Images / build-and-push (push) Successful in 37s
- Add agrarmonitor_tag_posteingang setting (default empty)
- When a document is not found in Agrarmonitor, move it back to Posteingang
  tag instead of skipping (if tagPosteingang is configured)
- Expose tagPosteingang in polling config API and settings UI

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 21:45:58 +02:00