L5 Berichte & Unterschrift: Server Actions und API v1

Actions mit moduleGuard("reports"); POST daily-report/completion-report, POST approve,
GET pdf sowie Dateiauslieferung für im Bericht referenzierte Dokumente.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-14 12:22:40 +02:00
co-authored by Claude Opus 5
parent 8a6fdd8f7a
commit 8f53df6208
8 changed files with 279 additions and 0 deletions
@@ -0,0 +1,11 @@
import { approveReport } from "@/server/services/reports/approve";
import { reportDto, withReportsApi } from "@/server/services/reports/http";
/** POST /api/v1/reports/:id/approve — team lead → team_approved, backoffice → approved (+ PDF job). */
export async function POST(_req: Request, { params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
return withReportsApi(["report:read"], async (ctx) => {
const report = await approveReport(ctx, { reportId: id });
return Response.json({ report: reportDto(report) });
});
}
@@ -0,0 +1,9 @@
import { fileResponse, openReportFile } from "@/server/services/reports/files";
import { withReportsApi } from "@/server/services/reports/http";
/** GET /api/v1/reports/:id/files/:documentId — photo/signature/logo referenced by the report snapshot. */
export async function GET(req: Request, { params }: { params: Promise<{ id: string; documentId: string }> }) {
const { id, documentId } = await params;
const download = new URL(req.url).searchParams.get("download") === "1";
return withReportsApi(["report:read"], async (ctx) => fileResponse(await openReportFile(ctx, id, documentId), { download }));
}
+9
View File
@@ -0,0 +1,9 @@
import { fileResponse, openReportFile } from "@/server/services/reports/files";
import { withReportsApi } from "@/server/services/reports/http";
/** GET /api/v1/reports/:id/pdf — the immutable PDF of an approved report (?download=1 for attachment). */
export async function GET(req: Request, { params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
const download = new URL(req.url).searchParams.get("download") === "1";
return withReportsApi(["report:read"], async (ctx) => fileResponse(await openReportFile(ctx, id, "pdf"), { download }));
}
@@ -0,0 +1,12 @@
import { createCompletionReport } from "@/server/services/reports/create";
import { readJson, reportDto, withReportsApi } from "@/server/services/reports/http";
/** POST /api/v1/work-orders/:id/completion-report — create (or return) the completion report draft; 422 + blockers if blocked. */
export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
return withReportsApi(["report:write"], async (ctx) => {
const body = await readJson(req);
const res = await createCompletionReport(ctx, { ...body, workOrderId: id } as Parameters<typeof createCompletionReport>[1]);
return Response.json({ report: reportDto(res.report), created: res.created }, { status: res.created ? 201 : 200 });
});
}
@@ -0,0 +1,12 @@
import { createDailyReport } from "@/server/services/reports/create";
import { readJson, reportDto, withReportsApi } from "@/server/services/reports/http";
/** POST /api/v1/work-orders/:id/daily-report — create (or return) the daily report draft. Body: { reportDate?, clientId? } */
export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
return withReportsApi(["report:write"], async (ctx) => {
const body = await readJson(req);
const res = await createDailyReport(ctx, { ...body, workOrderId: id } as Parameters<typeof createDailyReport>[1]);
return Response.json({ report: reportDto(res.report), created: res.created }, { status: res.created ? 201 : 200 });
});
}