L4 Einsatz mobil: Field-Services, Sync-API, Uploads und Tests

- services/field: Einsatz-Sessions (Anfahrt/Arbeit/Pause als TimeEntry-Segmente,
  eine aktive Session je User+Auftrag), Zeitkorrektur mit Recht + Grund + Audit,
  Checkliste, Material (Abweichung nur mit Begründung, Zusatzmaterial), Notizen,
  Fotos, Sprachnotizen (ohne Transkriptions-Processor Status disabled), Uploads
  (idempotent je Mandant), autorisierte Dokument-Auslieferung, Lesemodelle + Bundle
- services/sync: applyOperations mit Idempotenz, baseVersion-Konfliktprüfung,
  Registry für Ops anderer Lanes, lane-lokaler requireApiContext
- /api/v1/sync, /api/v1/uploads, /api/v1/field/bundle, /api/v1/field/documents/[id]
- lib/sync/ops.ts (Zod-Payloads je opType), lib/field/material-rules.ts
- Stubs mit Vertragssignatur: transitionWorkOrder (L2), storeFile (§4.3),
  getSiteHistory (L1)
- Processor image-derivatives + Registrierung, Audit-Entity-Labels
- Tests: test-einsatz-field (48 Prüfungen), test-einsatz-sync (38 Prüfungen)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-14 12:29:48 +02:00
co-authored by Claude Opus 5
parent bf4456718e
commit 952e74cddd
31 changed files with 2312 additions and 1 deletions
+14
View File
@@ -0,0 +1,14 @@
import { NextResponse } from "next/server";
import { syncRequestSchema } from "@/lib/sync/envelope";
import { applyOperations } from "@/server/services/sync/apply";
import { apiError, requireApiContext, withApi } from "@/server/services/sync/api-context";
/** POST /api/v1/sync — batch of offline/online operations (ARCHITEKTUR §4.6). */
export async function POST(req: Request) {
return withApi(req, async () => {
const ctx = await requireApiContext("field");
const body = syncRequestSchema.safeParse(await req.json().catch(() => null));
if (!body.success) return apiError("invalid", 400, "invalid sync request", body.error.issues.slice(0, 10));
return NextResponse.json(await applyOperations(ctx, body.data), { headers: { "Cache-Control": "no-store" } });
});
}