feat: ein Knopf synchronisiert Ernten, Chargen und Produkte zusammen

Bisher gab es zwei Knoepfe auf zwei Seiten, und ihre Reihenfolge war nicht
beliebig: eine Charge braucht ihre Ernte als Pflicht-Fremdschluessel, ein
Produkt entsteht je Abfuellung und braucht die zugehoerige Charge. Wer mit
den Produkten anfing, bekam reihenweise "wartet auf den Ernten-Sync" und
musste danach noch einmal klicken. Diese Reihenfolge gehoert in den Ablauf,
nicht in den Kopf der bedienenden Person.

syncApiCoreAction ersetzt beide Actions und laeuft Ernten -> Chargen ->
Produkte in einem Durchgang. "Alles synchronisieren" steht auf beiden Seiten
und springt danach dorthin zurueck, von wo er ausgeloest wurde. Das Ziel
kommt aus einem Formularfeld und wird gegen eine feste Liste geprueft — ein
ungeprueftes returnTo waere eine offene Weiterleitung.

Nebeneffekt: die Abfuellungen werden nur noch EINMAL von ApiCore geladen.
Vorher holten Chargen- und Produkt-Sync dieselbe Liste getrennt.

Knopf und Ergebnisanzeige liegen jetzt in einer gemeinsamen Komponente
(components/apicore-sync-panel.tsx) statt zweimal in zwei Seiten. Die
Ergebnisse stehen als ein Block beieinander, je eine Zeile fuer Ernten,
Chargen und Produkte, plus die beiden Hinweise (Abfuellung ohne Charge,
ausgelaufener Artikel) als Hinweis statt als Fehler.

Damit wurden zehn i18n-Schluessel ueberfluessig, die vorher die beiden
getrennten Anzeigen bedienten; sie sind aus dem Vertrag und beiden
Sprachdateien entfernt.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5 committed 2026-09-17 18:26:09 +02:00
1 parent 8f0f06fd13
commit 92ac826baa
8 files changed
+246 -213

No files matched your search

+10 -6
View File
@@ -194,11 +194,15 @@ Die serverseitige Prüfung läuft über `src/lib/turnstile.ts` gegen die Cloudfl
ApiCore ist das separate Imkerei-Verwaltungssystem (NestJS-Backend mit eigener Datenbank), in dem Völker, Ernten, Schleuderungen und Abfüllungen gepflegt werden. Wabenhain kann drei Datenarten daraus übernehmen, damit dieselbe Charge nicht zweimal von Hand eingetippt wird:
| ApiCore | Wabenhain | Knopf |
|---------|-----------|-------|
| `Harvest` (Ernte) | `Harvest` | `/admin/ernten` → „ApiCore-Synchronisierung" |
| `BottlingBatch` (Abfüllung) | `Batch` (Charge) | derselbe Knopf, läuft direkt nach den Ernten |
| `BottlingBatch` → je Abfüllung **ein** `Product` | `Product` (ein Jahrgang) | `/admin/produkte` → „ApiCore-Synchronisierung" |
**Ein Knopf für alles:** „Alles synchronisieren" steht sowohl unter `/admin/ernten` als auch unter `/admin/produkte` und löst denselben Vorgang aus — es geht danach nur auf die Seite zurück, von der er kam.
| Schritt | ApiCore | Wabenhain |
|---------|---------|-----------|
| 1 | `Harvest` (Ernte) | `Harvest` |
| 2 | `BottlingBatch` (Abfüllung) | `Batch` (Charge) |
| 3 | `BottlingBatch` → je Abfüllung **ein** `Product` | `Product` (ein Jahrgang) |
Die Reihenfolge ist zwingend, weil jede Stufe auf der vorherigen aufbaut: eine Charge braucht ihre Ernte als Pflicht-Fremdschlüssel, ein Produkt entsteht je Abfüllung und braucht die zugehörige Charge. Genau deshalb steckt sie im Ablauf und nicht im Kopf der bedienenden Person — getrennt ausgelöst musste man sie kennen und zweimal klicken, sonst meldete der erste Produkt-Lauf reihenweise fehlende Chargen. Nebenbei werden die Abfüllungen nur noch einmal von ApiCore geladen statt zweimal.
**Grundregel: ApiCore liefert die Rohdaten, Wabenhain trifft die Verkaufsentscheidungen.** Ein Sync überschreibt deshalb nie etwas, das im Admin bewusst gesetzt wurde:
@@ -211,7 +215,7 @@ ApiCore ist das separate Imkerei-Verwaltungssystem (NestJS-Backend mit eigener D
Ein bereits vorhandener Jahrgang wird übersprungen, nie aktualisiert: genau die Felder, die ApiCore liefert (Name, Beschreibung, Gewicht), zieht der Admin vor der Freischaltung von Hand nach, und ein Update würde diese Arbeit bei jedem weiteren Lauf wegwerfen. Soll ein Jahrgang wirklich frisch aus ApiCore kommen, löscht man ihn in Wabenhain und synchronisiert erneut.
**Reihenfolge:** die Charge muss schon da sein, sonst gäbe es nichts, woran der Jahrgang hängt. Erst „Ernten" synchronisieren, dann „Produkte" — fehlende Chargen meldet der Lauf als „wartet auf den Ernten-Sync", nicht als Fehler.
Findet sich zu einer Abfüllung keine Charge, meldet der Lauf das als Hinweis statt als Fehler — meist steht dahinter eine Ernte, die in ApiCore noch fehlt.
- **Chargen** werden ausschließlich neu angelegt, nie aktualisiert. Ein Update würde `remainingGlasses` überschreiben — das ist Wabenhains eigener, durch echte Verkäufe veränderter Lagerbestand.
- Charge und Ernte hängen von Anfang an am Jahrgang — er entsteht ja aus genau einer Abfüllung. Das ist die Verknüpfung, die die Herkunftsseite hinter dem QR-Code auf dem Glas füttert. Eine Abfüllung, die in ApiCore keinem Artikel zugeordnet ist (etwa Rohware für den Direktverkauf ab Hof), wird bewusst übergangen: daraus ließe sich kein Shop-Produkt bilden, es gäbe weder Namen noch Artikelnummer.
- Ein in ApiCore ausgelaufenes Produkt (`DISCONTINUED`) wird **nicht** automatisch deaktiviert: solange Gläser im Regal stehen, ist „nicht mehr produziert" kein Grund, den Abverkauf zu stoppen. Der Fall wird stattdessen im Ergebnis gemeldet.