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]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-09-28 11:10:07 +02:00
1 parent f263ec79f2
commit 0d23963f07
24 files changed
+941 -113

No files matched your search

+22 -5
View File
@@ -27,11 +27,15 @@ Verifikationsstufen: **CR** = durch Codeprüfung beurteilt, **AT** = automatisie
| 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, 524 Unit-Tests, 185 E2E-Tests (isolierte Postgres 17), `nest build` – grün.
- 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
@@ -56,8 +60,8 @@ Umgesetzt in Wabenhain `d38f430` (AT: Entscheidungslogik; CR: DB-Ablauf der Serv
## 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`, mit Rückfall auf die alten Endpunkte bei HTTP 404.
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 `APICORE_API_KEY` (Passwort-Login bleibt Rückfall).
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.
@@ -65,11 +69,24 @@ Umgesetzt in Wabenhain `d38f430` (AT: Entscheidungslogik; CR: DB-Ablauf der Serv
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**: Wabenhain meldet sich mit E-Mail/Passwort an. Ein
eigener, nur lesender API-Token-Mechanismus in ApiCore wäre sicherer – bewusst nicht ohne Absprache gebaut.
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`).