# Agrarmonitor Polling Service — Design **Datum:** 2026-05-23 **Branch:** Agrarmonitor --- ## Kontext Der Polling-Service prüft regelmäßig Paperless-Dokumente, die als "fertig für Agrarmonitor" markiert sind (Tag-ID konfigurierbar), gleicht sie mit Agrarmonitor-Eingangsrechnungen ab und aktualisiert sowohl Agrarmonitor (Eingangsdatum, Lieferscheinnummer) als auch Paperless (Korrespondent, Betrieb, Tags) sobald ein Buchungsdatum vorliegt. Logik basiert auf `ProcessEingaenge.cs` aus dem C#-Paperlessworker. --- ## Datenbankänderungen ### `Client`-Entity Neue nullable Spalte: ```typescript @Column({ type: 'int', nullable: true }) AgrarmonitorBetriebId: number | null; ``` Verknüpft einen Paperless-Betrieb (Client) mit seiner Agrarmonitor-BetriebId. Wird vom Polling-Service für die Dokumentzuordnung genutzt. ### `Setting`-Entity (neue Einträge, per Seed/upsert) Zwei neue Einträge identifiziert über das `Tag`-Feld: - `Tag = 'agrarmonitor_tag_fertig'`, Wert default `'4'` — Tag-ID für "fertig in Agrarmonitor" - `Tag = 'agrarmonitor_tag_verbucht'`, Wert default `'9'` — Tag-ID für "verbucht" --- ## Backend ### Neue Datei: `agrarmonitor-polling.service.ts` **Methoden:** - `runPolling(): Promise` — Haupt-Logik, synchron ausführbar - Automatischer Start via `@Cron(env.AGRARMONITOR_POLLING_CRON)` **Polling-Logik (sequenziell):** 1. Tag-IDs aus `Setting`-Tabelle lesen (Tag-Fertig, Tag-Verbucht) 2. `agrarmonitorService.getClient()` → Connector holen 3. `client.fetchCustomers()` → aktive Lieferanten (`ist_lieferant=1`, `ist_aktiv=1`) als Paperless-Korrespondenten synchronisieren 4. Paperless-Dokumente mit Tag-Fertig laden 5. Pro Dokument mit `interneBelegnummer`: - `client.eingangsrechnungenLivesearch(belegnummer)` aufrufen - Kein Treffer → überspringen - Mehrere Treffer → Fehler loggen, überspringen - Kein `eingangsDatum` + Paperless hat `Eingangsdatum` → `client.setEingangsdatum()` aufrufen - `buchungsDatum` vorhanden → Paperless-Dokument aktualisieren: - Korrespondenten über `AgrarmonitorBetriebId`-Mapping setzen - Betrieb/Owner/Gruppe aus Client-Tabelle übernehmen - Tag-Fertig entfernen, Tag-Verbucht hinzufügen - `paperlessService.updateDocument()` aufrufen - 500ms Pause zwischen Dokumenten (API-Schonung) 6. `PollingResult` zurückgeben: `{processed, updated, skipped, errors}` **Abhängigkeiten:** `AgrarmonitorService`, `PaperlessService`, `Repository`, `Repository` ### `AgrarmonitorModule` — Erweiterungen ```typescript imports: [ TypeOrmModule.forFeature([Setting, Client]), PaperlessModule, ScheduleModule, // bereits global registriert ] providers: [AgrarmonitorService, AgrarmonitorPollingService] ``` ### Neue Endpoints (in `AgrarmonitorController`) | Method | Route | Beschreibung | |--------|-------|-------------| | `GET` | `/api/agrarmonitor/polling-config` | Tag-IDs lesen | | `PUT` | `/api/agrarmonitor/polling-config` | Tag-IDs speichern | | `POST` | `/api/agrarmonitor/run-polling` | Polling manuell auslösen | Alle Routen: `@RequirePermissions(Permission.MANAGE_SETTINGS)` ### `.env.example` — neuer Eintrag ``` AGRARMONITOR_POLLING_CRON=0 */30 * * * * # alle 30 Minuten ``` --- ## Frontend ### Agrarmonitor-Tab (bestehend erweitern) **Neuer Abschnitt "Polling-Konfiguration":** - Input "Tag: Fertig in Agrarmonitor" (Zahl, lädt aus `/api/agrarmonitor/polling-config`) - Input "Tag: Verbucht" (Zahl) - Speichern-Button → `PUT /api/agrarmonitor/polling-config` **Neuer Abschnitt "Polling ausführen":** - Button "Jetzt ausführen" → `POST /api/agrarmonitor/run-polling` - Ergebnisanzeige: "X verarbeitet, X aktualisiert, X Fehler" ### "Benutzer & Betriebe"-Tab (bestehend erweitern) In der Client-Tabelle: neue Spalte "Agrarmonitor-BetriebId" (inline editierbar, Zahl oder leer). Speichern via `PUT /api/settings/clients/:id` (neuer Endpoint oder bestehenden erweitern). --- ## Datenfluss ``` Cron / manueller Trigger → AgrarmonitorPollingService.runPolling() → AgrarmonitorService.getClient() (Connector) → PaperlessService.getDocuments() (Tag-Fertig-Filter) → pro Dokument: → client.eingangsrechnungenLivesearch() → client.setEingangsdatum() (falls nötig) → client.fetchCustomers() + PaperlessService.updateDocument() ``` --- ## Fehlerbehandlung - Einzelne Dokument-Fehler werden geloggt, überspringen den Eintrag, brechen den gesamten Lauf nicht ab - Connector-Fehler (Verbindung zu Agrarmonitor) brechen den Lauf ab, geben `{error}` zurück - Ergebnis wird immer als strukturiertes Objekt zurückgegeben (nie 500) --- ## Nicht im Scope - E-Mail-Benachrichtigung bei Fehlern (kommt ggf. später via Postprocessing) - Rückgängig machen von Buchungen - Polling-Log in der Datenbank (Ergebnis nur in Backend-Logs + API-Response)