docs: add Agrarmonitor polling design plans
Build and Push Multi-Platform Images / build-and-push (push) Successful in 57s

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-23 15:09:53 +02:00
parent e5271fc035
commit 1d11d8a3bd
2 changed files with 1493 additions and 0 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,135 @@
# 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<PollingResult>` — 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<Setting>`, `Repository<Client>`
### `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)