Files
ApiCore/docs/pruefung-2026-09.md
T
Nick SzaunigandClaude Opus 5.5 0d23963f07 feat(export): Bestandsbuch für Tierarzneimittel nach Art. 108 VO (EU) 2019/6
- Treatment records supplier, purchase proof and prescribing vet, and
  keeps the apiary the colony stood at on the treatment day.
- Holder name and address in site settings (never in public output;
  regression test in public.service.spec).
- treatment-log.pdf: one sheet per apiary, identical treatments grouped,
  missing mandatory fields printed as FEHLT, uncertain locations marked.
- Shared PDF table helpers extracted from the record card.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-28 11:10:07 +02:00

93 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Prüfung & Verbesserung ApiCore / Wabenhain – Arbeitsstand September 2026
Branch in ApiCore: `improve/integrity-sync-audhd` (nicht gemergt, nicht gepusht).
Wabenhain: Commits direkt auf `main` (nicht gepusht).
Verifikationsstufen: **CR** = durch Codeprüfung beurteilt, **AT** = automatisiert getestet,
**MP** = manuell im Browser geprüft (Desktop 1366 px + Mobil 390 px), **NV** = nicht verifiziert.
## Prüfmatrix
| Funktion | Erwartetes Verhalten | Befund (vorher) | Prio | Umsetzung | Status |
|---|---|---|---|---|---|
| Abfüllung bucht Honigmenge | Menge nie doppelt/zu viel abgebucht | Menge außerhalb der Transaktion gelesen, Absolutwert geschrieben → parallele Abfüllungen überbuchen, Abzug geht verloren | Kritisch | Bedingtes `UPDATE … WHERE amount >= x` in Transaktion; Korrektur/Löschen per Increment | AT |
| Lagerentnahme | Kein negativer Bestand | Ausgang ohne Bestandsprüfung | Hoch | Bedingtes Decrement, 400 bei Unterdeckung | AT |
| Völker umsetzen/teilen/vereinigen | Lebende Völker bearbeitbar | Nur Status `ACTIVE` erlaubt – schwache/weisellose Völker gesperrt (Vereinigung weiselloser Völker unmöglich) | Hoch | Nur Endzustände (aufgelöst, verkauft, tot, vereinigt) sperren; Selbst-Vereinigung + doppeltes Auflösen abgelehnt | AT |
| Königin umweiseln | Wechsel A → B möglich, Historie korrekt | Altes Volk behielt `currentQueenId` → Unique-Konflikt; Zuweisung an aufgelöste Völker möglich | Hoch | Altes Volk gibt Königin frei (→ weisellos), weiselloses Volk mit Königin → aktiv | AT |
| Aufgabe erledigen | Idempotent, Status/Zeit konsistent | Doppelklick verschob `completedAt`, Webhook doppelt; Wiedereröffnen behielt `completedAt` | Mittel | Zweites Erledigen = No-op; `completedAt` folgt Status | AT |
| Wiederkehrende Aufgaben | Fällige Aufgabe mit Termin | Erzeugte Aufgaben ohne Fälligkeit; verpasste Wochen einzeln nachgeholt | Mittel | Fälligkeit = Termin; verpasste Termine ergeben EINE Erinnerung | AT |
| Automatisierungen | Eine offene Aufgabe je Regel und Objekt | Jede Auslösung (z. B. jede Entnahme unter Mindestbestand) erzeugte neue Aufgabe + Warnung | Hoch | Kennzeichnung `sourceAutomationTrigger`, Duplikatsperre; optional `config.dueInDays` | AT |
| Varroa-Schwelle | Vergleich nur in gleicher Einheit | Probenzählwerte (Puderzucker/Auswaschen) und Brettzählung ohne Zeitraum gegen „Milben/Tag“ verglichen | Hoch | Nur natürlicher Milbenfall mit Tagesrate | AT |
| Offline-Warteschlange | Wiederholung erzeugt keine Duplikate | Verlorene Antwort → erneutes Senden → doppelte Durchsicht/Ernte + Automatik-Aufgabe | Hoch | `Idempotency-Key` (Opt-in je Endpunkt), Schlüssel im Queue-Eintrag | AT |
| Start mehrerer Replikas | Beide Pods starten | Berechtigungs-Sync auf leerer DB: Unique-Fehler, Pod-Absturz | Mittel | `createMany … skipDuplicates` | AT |
| Eingabeplausibilität | Serverseitige Grenzen | Skalen 1–5 nur im UI; doppelte Völker in Ernte → 500; Netto > Brutto möglich | Mittel | DTO-Grenzen, `ArrayUnique`, Gewichtsprüfung | AT |
| Wabenhain: Ernte-Beschreibung | Keine internen Daten öffentlich | Interne ApiCore-Ernte-Notizen in Shoptext kopiert | Hoch (Sicherheit) | Notizen nicht mehr übernommen | AT |
| Wabenhain: Entwürfe | Admin-Korrekturen bleiben | Jeder Sync überschrieb unveröffentlichte Entwürfe | Hoch | Im Admin bearbeitete Entwürfe (Audit-Log) geschützt, gemeldet | CR |
| Wabenhain: Abruf | Kein Hängen, Fehler ≠ leere Liste | Kein Timeout | Mittel | 20-s-Timeout; Tests: Fehler/Strukturfehler brechen ab | AT |
| Wabenhain: Sichtbarkeit | Letzter Lauf erkennbar | Nur direkt nach Klick | Niedrig | „Letzter vollständiger Lauf“ im Panel | CR |
| Start-/Heute-Ansicht | Nächster Schritt klar | Kennzahlen-Kacheln statt Aufgaben | Hoch (UX) | „Als Nächstes“ + „Außerdem heute“, Rückgängig | AT (Logik) + MP |
| Bewegung | Einstellbar, System respektiert | Nur `motion-safe`-Klassen | Mittel | Voll/Dezent/Aus je Gerät | MP |
| Etiketten-Schnittmarken | Anlegekante für Papierschneider | Keine Marken, Zuschnitt nach Augenmaß | Mittel | Haarlinien-Marken im Blattrand an jeder Schnittkante, je Vorlage abschaltbar | AT + MP |
| Folge-Kontrollen | Nachkontrolle nach Maßnahme | Keine | Mittel | Regeln „Nach Behandlung/Fütterung“, standardmäßig aus, Frist „Fällig nach (Tagen)“ | AT |
| Stockkarte je Volk | Vollständige Historie als PDF | Nur Jahresbericht | Mittel | `colony-record-card.pdf`, optional je Jahr | AT + MP |
| Bestandsbuch Tierarzneimittel | Pflichtangaben Art. 108 VO (EU) 2019/6 | Lieferant, Erwerbsbeleg, Tierarzt, Standort am Behandlungstag, Halter fehlten | Hoch (Recht) | Felder ergänzt, Stand je Behandlung gespeichert, PDF je Stand, fehlende Pflichtangaben als „FEHLT“ | AT + MP |
| E2E-Tests | Grün | 23 rot (1 Produktfehler, 3 veraltete Tests) | – | behoben | AT |
## Prüfungen (letzter Lauf)
- Backend: Typecheck, ESLint, 539 Unit-Tests, 203 E2E-Tests (isolierte Postgres 17), `nest build` – grün.
- Frontend: Typecheck, ESLint, 44 Tests, `next build` – grün. Design-Detektor (impeccable): 0 Befunde.
- Wabenhain: Typecheck, ESLint (geänderte Dateien), 538 Tests – grün.
- Manuell (Browser, lokale Demo-Daten): Login, Heute-Ansicht Desktop/Mobil, Abhaken in Liste und
„Als Nächstes“, Rückgängig (stellt vorherigen Status her), Profil-Bewegung „Aus“ wirkt, Aufgabenliste mobil.
## Zuständigkeit (Quelle der Wahrheit)
ApiCore und Wabenhain bleiben getrennte Pakete; Verbindung nur über die HTTP-API von ApiCore.
| Information | Maßgeblich | Sync-Verhalten (Wabenhain) |
|---|---|---|
| Stand, Völker, Durchsichten, Behandlungen, interne Notizen | ApiCore | wird nicht übertragen |
| Ernte: Datum, Jahr | ApiCore | bei jedem Lauf aktualisiert |
| Ernte: Sorte, Ort, Text | Wabenhain | nur Vorbelegung unbearbeiteter Entwürfe |
| Charge: Losnummer, Abfülldatum, MHD, abgefüllte Gläser | ApiCore | bei jedem Lauf aktualisiert (Zuordnung über ApiCore-ID) |
| Charge: Shop-Bestand, Bilder, Ernte-Zuordnung | Wabenhain | nie geändert; Bestand > Abfüllmenge wird gemeldet |
| Produkt: Name, Text, Gewicht, Preis, Bilder, Sichtbarkeit | Wabenhain | nur beim ersten Anlegen vorbelegt |
| Bestellungen, Kunden, Bewertungen | Wabenhain | nie Richtung ApiCore |
Umgesetzt in Wabenhain `d38f430` (AT: Entscheidungslogik; CR: DB-Ablauf der Server-Action).
## Nächste Schritte
1. ~~Zuständigkeit pro Feld im Sync~~ – erledigt.
2. ~~Eigene, versionierte Sync-Schnittstelle~~ – erledigt: `GET /api/v1/shop-sync` (Vertragsversion 1, Feld-Allowlist, Berechtigung `shop-sync:read`). Bewusst ohne „geändert seit“: kleine Datenmenge, Voll-Abgleich heilt verpasste Läufe selbst. Wabenhain nutzt sie seit `5642f0c`; seit `3bc0693` ausschließlich (kein Rückfall auf andere Endpunkte).
3. ~~Nur-Lese-API-Schlüssel~~ – erledigt: Verwaltung → Automatisierung → API-Schlüssel. Nur Hash gespeichert, Klartext einmalig angezeigt, Scope nur `shop-sync:read`, widerrufbar, optional befristet, „zuletzt benutzt“ sichtbar. Wabenhain liest nur noch `APICORE_API_KEY`; der Passwort-Login ist entfernt (`3bc0693`).
4. Ende-zu-Ende-Test beider Systeme gegen Test-Instanzen.
5. Merge/Push.
## Offen / Entscheidungen
1. **Wabenhain-Sync gegen echtes ApiCore** nicht Ende zu Ende getestet (keine Zugangsdaten/Testinstanz
verbunden). Mapping, Client und Schutzlogik sind unit-getestet bzw. per Code geprüft.
2. ~~Service-Konto statt API-Schlüssel~~ – erledigt: nur noch API-Schlüssel mit Scope `shop-sync:read`.
3. **Status „blockiert“** existiert im Aufgabenmodell nicht. Nicht ergänzt (Schemaänderung + fachliche Definition nötig).
4. **Produktbeschreibung** aus ApiCore wird in Wabenhain übernommen und auf der öffentlichen
Herkunftsseite gezeigt – gilt damit als öffentlich. Falls sie intern gemeint ist: klären.
5. Automatische Vorschläge haben standardmäßig **keinen Termin** (keine erfundene Frist);
je Regel über `config.dueInDays` einstellbar.
6. Übrige Formulare/Seiten wurden nicht einzeln überarbeitet (Fokus: Heute, Aufgaben, Profil).
## Bestandsbuch (Tierarzneimittel)
Grundlage: Art. 108 VO (EU) 2019/6, abgeglichen mit dem Muster-Formblatt des Veterinäramts Kassel
(Stand März 2026) und der FAQ des LGL Bayern. Gilt für jedes Tierarzneimittel, auch freiverkäufliche Säuren;
Aufbewahrung 5 Jahre.
- Export: Stand → „Bestandsbuch {Jahr}“ (`/export/treatment-log.pdf?year=&apiaryId=`), ein Blatt je Stand.
- Gleiche Behandlungen mehrerer Völker (Mittel, Charge, Menge, Zeitraum, Bezug) stehen in einer Zeile.
- Standort am Behandlungstag: seit dieser Version mit der Behandlung gespeichert (`Treatment.apiaryId`).
Für ältere Einträge aus der Umsetz-Historie abgeleitet; ist das unsicher, markiert `*` die Zeile.
- Fehlende Pflichtangaben erscheinen als „FEHLT“ statt leer – das Blatt sieht nie vollständiger aus, als es ist.
- Name und Anschrift des Halters: Verwaltung → Automatisierung → Stammdaten & Web. Erscheinen nur im
Bestandsbuch; die öffentliche Chargen-Seite gibt sie nachweislich nicht aus (Test in `public.service.spec.ts`).