fix(erp-export): strict seit format, DB clock for settle window, movement index, one scope per key
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
53fab102f2
commit
25d95b0473
19 files changed
+426
-57
No files matched your search
@@ -18,14 +18,14 @@ Der ERPNext-Importer wird genau nach diesem Dokument gebaut. Änderungen am Vert
|
||||
| Absender | Optional nur von bestimmten IP-Adressen/Netzen (Allowlist am Schlüssel). |
|
||||
| Fehlversuche | Nach 20 ungültigen Schlüsseln (unbekannt, widerrufen, abgelaufen) innerhalb von 15 Minuten bekommt diese IP-Adresse (bei IPv6 das ganze /64-Netz) für weitere ungültige Schlüssel 15 Minuten lang `429`; die Zählung gilt für alle ApiCore-Instanzen gemeinsam. Ein gültiger Schlüssel wird davon nie abgewiesen, auch nicht von einer gesperrten Adresse – sonst könnte jeder, der dieselbe Adresse nutzt oder vortäuscht, ERPNext oder den Shop aussperren. Nicht wie ein Schlüssel aufgebaute Tokens (`ack_…` im falschen Format) bekommen sofort `401` und zählen nicht. Zusätzlich gilt das allgemeine Limit von 100 Anfragen je Minute und IP. |
|
||||
| Protokoll | Abgelehnte Schlüssel werden mit Grund, Präfix (`ack_abcd1234`) und IP protokolliert, nie mit dem Schlüssel. Query-Strings landen nicht im Log. |
|
||||
| Trennung | Eigener Schlüssel nur für ERPNext. Der Wabenhain-Schlüssel (`shop-sync:*`) kommt an diese Endpunkte nicht heran und umgekehrt. |
|
||||
| Trennung | Eigener Schlüssel nur für ERPNext. Ein neuer Schlüssel trägt genau ein Recht; einen Schlüssel mit mehreren Rechten lehnt ApiCore beim Anlegen mit `400` ab (früher ausgestellte bleiben gültig). Der Wabenhain-Schlüssel (`shop-sync:*`) kommt an diese Endpunkte nicht heran und umgekehrt. |
|
||||
|
||||
Benutzer mit dem Recht `erp-inventory:read` (z. B. die Rolle Administrator) können die Endpunkte mit ihrem Login ebenfalls lesen, etwa zum Prüfen.
|
||||
|
||||
### Schlüssel anlegen (Inhaber)
|
||||
|
||||
1. ApiCore → Verwaltung → Automatisierung → API-Schlüssel.
|
||||
2. Name: `ERPNext`. Unter „Berechtigung“ **„ERP-Inventar lesen“** wählen (vorausgewählt ist „Shop lesen“). Ein Schlüssel trägt genau ein Recht; einen Schlüssel mit Lese- und Buchungsrecht lehnt ApiCore ab.
|
||||
2. Name: `ERPNext`. Unter „Berechtigung“ **„ERP-Inventar lesen“** wählen (vorausgewählt ist „Shop lesen“). Ein Schlüssel trägt genau ein Recht; einen Schlüssel mit mehreren Rechten lehnt ApiCore ab.
|
||||
3. IP-Adressen: die Adresse, von der ERPNext ApiCore erreicht (siehe [Rollout](#11-rollout)). Unbekannt? Leer lassen, einmal abrufen, dann am Schlüssel „Adressen ändern“ und die zuletzt benutzte Adresse übernehmen.
|
||||
4. Schlüssel kopieren und **nur** als Kubernetes-Secret für den ERPNext-Abgleich hinterlegen (nicht in `config.json`, nicht ins Repo, nicht in Tickets).
|
||||
|
||||
@@ -106,7 +106,7 @@ Hinweise für den Abgleich:
|
||||
|
||||
- `remainingQuantity` ist der Sollwert für den Bestandsabgleich (Stock Reconciliation) im Lager „Imkerei (ApiCore)“. Er ist **live**: er enthält alle Bewegungen bis zum Abruf. `/bewegungen` liefert dagegen nur Bewegungen, die älter als 5 Minuten sind. Eine Buchung der letzten Minuten steckt also schon im Rest, ihre Bewegung kommt erst im nächsten Lauf. Der Importer darf deshalb den Rest nicht aus den gelieferten Bewegungen nachrechnen und mit `remainingQuantity` vergleichen; Sollwert ist immer `remainingQuantity`, die Bewegungen dienen nur der Einordnung (Vertrauensschrank, Eigenbedarf).
|
||||
- Wird `initialQuantity` in ApiCore korrigiert, ändert sich der Rest sofort mit.
|
||||
- Eine irrtümlich angelegte Herstellungscharge ohne Bewegungen kann in ApiCore gelöscht werden; sie fehlt dann in der Liste und ist im Spiegel auf 0 zu setzen. Abfüllungen und Chargen mit Bewegungen werden nie gelöscht.
|
||||
- Eine irrtümlich angelegte Abfüllung oder Herstellungscharge ohne Bewegungen kann in ApiCore gelöscht werden; sie fehlt dann in der Liste und ist im Spiegel auf 0 zu setzen. Abfüllungen und Chargen mit Bewegungen werden nie gelöscht.
|
||||
|
||||
## 5. `GET /bewegungen` – Bestandsbewegungen
|
||||
|
||||
@@ -114,7 +114,7 @@ Jede Bestandsänderung einer Abfüllung oder Herstellungscharge, nie gelöscht (
|
||||
|
||||
| Parameter | Typ | Bedeutung |
|
||||
|---|---|---|
|
||||
| `seit` | ISO-Zeitpunkt oder -Datum | Nur Bewegungen ab diesem Zeitpunkt (einschließlich). Für den ersten Lauf, z. B. `seit=2026-01-01T00:00:00Z`. Nicht zusammen mit `nach`. |
|
||||
| `seit` | `JJJJ-MM-TT` (Mitternacht UTC) oder `JJJJ-MM-TTThh:mm[:ss[.sss]]` mit `Z` oder `±hh:mm`; Jahr 2000–2100 | Nur Bewegungen ab diesem Zeitpunkt (einschließlich). Für den ersten Lauf, z. B. `seit=2026-01-01T00:00:00Z`. Nicht zusammen mit `nach`. Andere ISO-8601-Formen (Wochen, Ordinaltage, ohne Striche, Uhrzeit ohne Zone) → `400`; `+` im Offset URL-kodieren (`%2B`). |
|
||||
| `nach` | Cursor | Weiterlesen hinter der zuletzt gelieferten Bewegung |
|
||||
| `limit` | 1–500 | wie oben |
|
||||
|
||||
@@ -180,7 +180,7 @@ Fehlerantworten sind JSON mit `statusCode` und `message` (bei Validierungsfehler
|
||||
|
||||
| Status | Wann | Verhalten des Importers |
|
||||
|---|---|---|
|
||||
| `400` | `limit` außerhalb 1–500 oder keine Ganzzahl; `nach` kein gültiger Cursor oder von einem anderen Endpunkt; `seit` kein ISO-Zeitpunkt; `seit` und `nach` zusammen; `jahr` außerhalb 1900–2100; unbekannter Parameter | Programmfehler – Schritt abbrechen, im Cockpit melden |
|
||||
| `400` | `limit` außerhalb 1–500 oder keine Ganzzahl; `nach` kein gültiger Cursor oder von einem anderen Endpunkt; `seit` nicht in einer der Formen aus Abschnitt 5 oder außerhalb 2000–2100; `seit` und `nach` zusammen; `jahr` außerhalb 1900–2100; unbekannter Parameter | Programmfehler – Schritt abbrechen, im Cockpit melden |
|
||||
| `401` | Schlüssel fehlt, ist fehlerhaft, unbekannt, widerrufen oder abgelaufen | Schritt abbrechen; Hinweis „ApiCore-Schlüssel prüfen“ |
|
||||
| `403` | Schlüssel ohne `erp-inventory:read`, oder IP-Adresse nicht in der Allowlist des Schlüssels | Schritt abbrechen; Hinweis „Recht bzw. Adressen am Schlüssel prüfen“ |
|
||||
| `429` | ungültiger Schlüssel von einer Adresse mit zu vielen Fehlversuchen, oder allgemeines Anfragelimit überschritten | Schritt abbrechen, im nächsten Lauf erneut |
|
||||
|
||||
Reference in new issue
Block a user