docs: add Agrarmonitor polling design plans
Build and Push Multi-Platform Images / build-and-push (push) Successful in 57s
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:
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)
|
||||||
Reference in New Issue
Block a user