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:
Nick SzaunigandClaude Opus 5.5 committed 2026-10-13 23:24:05 +02:00
1 parent 53fab102f2
commit 25d95b0473
19 files changed
+426 -57

No files matched your search

@@ -69,15 +69,29 @@ describe("ApiKeysTab", () => {
expect(hints).toHaveLength(1);
expect(hints[0]).toHaveTextContent(HINT);
expect(hints[0]).toHaveTextContent(/zwei getrennte Schlüssel/);
expect(hints[0]).toHaveTextContent(/in Wabenhain eintragen/);
});
it("also marks a key that reads the ERP mirror and books", async () => {
it("also marks a key that reads the ERP mirror and books, naming both systems", async () => {
vi.mocked(fetchApiKeys).mockResolvedValue([
makeKey("mixed", ["erp-inventory:read", "shop-sync:write"]),
]);
renderTab();
expect(await screen.findByRole("status")).toHaveTextContent(HINT);
const hint = await screen.findByRole("status");
expect(hint).toHaveTextContent(HINT);
expect(hint).toHaveTextContent(/in ERPNext bzw. Wabenhain eintragen/);
});
it("marks an older key with several read scopes", async () => {
vi.mocked(fetchApiKeys).mockResolvedValue([
makeKey("reads", ["shop-sync:read", "erp-inventory:read"]),
]);
renderTab();
const hint = await screen.findByRole("status");
expect(hint).toHaveTextContent(/mehrere Rechte/);
expect(hint).not.toHaveTextContent(HINT);
});
it("shows no hint when every key has one scope", async () => {
@@ -10,7 +10,7 @@ import {
API_KEY_SCOPE_LABELS,
API_KEY_SCOPES,
apiKeyScopeLabel,
hasReadAndWriteScopes,
separationAdvice,
} from "@/lib/api-keys/scopes";
import { ChoiceGroup, choiceOptions } from "@/components/form/choice-group";
import { Button } from "@/components/ui/button";
@@ -259,6 +259,7 @@ export function ApiKeysTab() {
<ul className="flex flex-col gap-2">
{keys.map((apiKey) => {
const state = keyState(apiKey);
const advice = state.active ? separationAdvice(apiKey.scopes) : null;
return (
<li
key={apiKey.id}
@@ -278,11 +279,9 @@ export function ApiKeysTab() {
</Badge>
))}
</span>
{state.active && hasReadAndWriteScopes(apiKey) ? (
{advice ? (
<p role="status" className="text-xs text-muted-foreground">
Dieser Schlüssel darf lesen und buchen. Sicherer sind zwei getrennte
Schlüssel: einer zum Lesen, einer zum Buchen. Neue Schlüssel anlegen, in
Wabenhain eintragen, dann diesen widerrufen.
{advice}
</p>
) : null}
<span className="text-xs text-muted-foreground">